/*-------------------------------------------------------------------------
 *
 * lock.c
 *	  POSTGRES 主要锁机制
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/storage/lmgr/lock.c
 *
 * NOTES
 *	  锁表是一个共享内存散列表。当进程尝试获取与现有锁发生冲突的类型的锁时，
 *	  它将使用 storage/lmgr/proc.c 中的例程进入睡眠。
 *
 *	  在大多数情况下，此代码应通过 lmgr.c 或其他锁管理模块调用，而不是直接调用。
 *
 *	接口：
 *
 *	InitLocks(), GetLocksMethodTable(), GetLockTagsMethodTable(),
 *	LockAcquire(), LockRelease(), LockReleaseAll(),
 *	LockCheckConflicts(), GrantLock()
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <signal.h>
#include <unistd.h>

#include "access/transam.h"
#include "access/twophase.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "pg_trace.h"
#include "pgstat.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/sinvaladt.h"
#include "storage/spin.h"
#include "storage/standby.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/resowner_private.h"


/* 此配置变量用于设置锁表大小 */
int			max_locks_per_xact; /* 由guc.c设置 */

#define NLOCKENTS() \
	mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))


/*
 * 定义标准锁方法语义的数据结构。
 *
 * 冲突表定义了各种锁模式的语义。
 */
static const LOCKMASK LockConflicts[] = {
	0,

	/* 访问共享锁 */
	LOCKBIT_ON(AccessExclusiveLock),

	/* 行共享锁 */
	LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

	/* 行独占锁 */
	LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
	LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

	/* 共享更新独占锁 */
	LOCKBIT_ON(ShareUpdateExclusiveLock) |
	LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
	LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

	/* 共享锁 */
	LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
	LOCKBIT_ON(ShareRowExclusiveLock) |
	LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

	/* ShareRowExclusiveLock */
	LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
	LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
	LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

	/* ExclusiveLock */
	LOCKBIT_ON(RowShareLock) |
	LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
	LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
	LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

	/* AccessExclusiveLock */
	LOCKBIT_ON(AccessShareLock) | LOCKBIT_ON(RowShareLock) |
	LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
	LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
	LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock)

};

/* 锁模式的名称，用于调试打印 */
static const char *const lock_mode_names[] =
{
	"INVALID",
	"AccessShareLock",
	"RowShareLock",
	"RowExclusiveLock",
	"ShareUpdateExclusiveLock",
	"ShareLock",
	"ShareRowExclusiveLock",
	"ExclusiveLock",
	"AccessExclusiveLock"
};

#ifndef LOCK_DEBUG
static bool Dummy_trace = false;
#endif

static const LockMethodData default_lockmethod = {
	MaxLockMode,
	LockConflicts,
	lock_mode_names,
#ifdef LOCK_DEBUG
	&Trace_locks
#else
	&Dummy_trace
#endif
};

static const LockMethodData user_lockmethod = {
	MaxLockMode,
	LockConflicts,
	lock_mode_names,
#ifdef LOCK_DEBUG
	&Trace_userlocks
#else
	&Dummy_trace
#endif
};

/*
 * 从锁方法 ID 到锁表数据结构的映射
 */
static const LockMethod LockMethods[] = {
	NULL,
	&default_lockmethod,
	&user_lockmethod
};


/* 当锁被持久化时写入 2PC 状态文件的记录 */
typedef struct TwoPhaseLockRecord
{
	LOCKTAG		locktag;
	LOCKMODE	lockmode;
} TwoPhaseLockRecord;


/*
 * 我们相信被使用的快速路径锁槽的数量。这个数量可能高于实际值，
 * 如果其他后台将我们的锁转移到主锁表，但它绝对不会低于实际值，
 * 因为仅我们能代表自己获取锁。
 */
static int	FastPathLocalUseCount = 0;

/*
 * 标志，以指示该后端是否持有关系扩展锁。
 * 此标志用于确保在持有关系扩展锁时，我们不会尝试在任何其他对象上获取重型锁。
 * 这一限制意味着关系扩展锁永远不会参与死锁循环，因为我们在获取此锁后永远不会等待任何其他重型锁。
 *
 * 这样的限制对于关系扩展锁是可以的，因为与其他重型锁不同，
 * 这些锁不会持续到事务结束。它们是在短时间内获取的，
 * 用于扩展特定关系，然后被释放。
 */
static bool IsRelationExtensionLockHeld PG_USED_FOR_ASSERTS_ONLY = false;

/* 操作 proc->fpLockBits 的宏 */
#define FAST_PATH_BITS_PER_SLOT			3
#define FAST_PATH_LOCKNUMBER_OFFSET		1
#define FAST_PATH_MASK					((1 << FAST_PATH_BITS_PER_SLOT) - 1)
#define FAST_PATH_GET_BITS(proc, n) \
	(((proc)->fpLockBits >> (FAST_PATH_BITS_PER_SLOT * n)) & FAST_PATH_MASK)
#define FAST_PATH_BIT_POSITION(n, l) \
	(AssertMacro((l) >= FAST_PATH_LOCKNUMBER_OFFSET), \
	 AssertMacro((l) < FAST_PATH_BITS_PER_SLOT+FAST_PATH_LOCKNUMBER_OFFSET), \
	 AssertMacro((n) < FP_LOCK_SLOTS_PER_BACKEND), \
	 ((l) - FAST_PATH_LOCKNUMBER_OFFSET + FAST_PATH_BITS_PER_SLOT * (n)))
#define FAST_PATH_SET_LOCKMODE(proc, n, l) \
	 (proc)->fpLockBits |= UINT64CONST(1) << FAST_PATH_BIT_POSITION(n, l)
#define FAST_PATH_CLEAR_LOCKMODE(proc, n, l) \
	 (proc)->fpLockBits &= ~(UINT64CONST(1) << FAST_PATH_BIT_POSITION(n, l))
#define FAST_PATH_CHECK_LOCKMODE(proc, n, l) \
	 ((proc)->fpLockBits & (UINT64CONST(1) << FAST_PATH_BIT_POSITION(n, l)))

/*
 * 快速路径锁机制仅涉及由绑定到数据库的后端对
 * 非共享关系的关系锁。快速路径机制主要是为了加速获取和释放
 * 很少冲突的锁。由于 ShareUpdateExclusiveLock 是自冲突的，
 * 它无法使用快速路径机制；但它也与任何使用该机制的锁不冲突，
 * 因此我们可以完全忽略它。
 */
#define EligibleForRelationFastPath(locktag, mode) \
	((locktag)->locktag_lockmethodid == DEFAULT_LOCKMETHOD && \
	(locktag)->locktag_type == LOCKTAG_RELATION && \
	(locktag)->locktag_field1 == MyDatabaseId && \
	MyDatabaseId != InvalidOid && \
	(mode) < ShareUpdateExclusiveLock)
#define ConflictsWithRelationFastPath(locktag, mode) \
	((locktag)->locktag_lockmethodid == DEFAULT_LOCKMETHOD && \
	(locktag)->locktag_type == LOCKTAG_RELATION && \
	(locktag)->locktag_field1 != InvalidOid && \
	(mode) > ShareUpdateExclusiveLock)

static bool fc_FastPathGrantRelationLock(Oid fc_relid, LOCKMODE fc_lockmode);
static bool fc_FastPathUnGrantRelationLock(Oid fc_relid, LOCKMODE fc_lockmode);
static bool fc_FastPathTransferRelationLocks(LockMethod fc_lockMethodTable,
										  const LOCKTAG *fc_locktag, uint32 fc_hashcode);
static PROCLOCK *fc_FastPathGetRelationLockEntry(LOCALLOCK *fc_locallock);

/*
 * 为了使快速路径锁机制工作，我们必须有某种方法
 * 来防止在可能存在冲突锁时使用快速路径。
 * 我们将锁标识空间分为 FAST_PATH_STRONG_LOCK_HASH_PARTITIONS，
 * 并维护每个分区中“强”锁的整数计数。当存在任何“强”锁时（这
 * 希望不太频繁），无法使用快速路径机制，我们
 * 必须退回到将匹配锁直接推送到主锁表的较慢方法。
 *
 * 死锁检测器对快速路径机制一无所知，
 * 因此可能涉及死锁的任何锁必须从快速路径队列转移到主锁表。
 */

#define FAST_PATH_STRONG_LOCK_HASH_BITS			10
#define FAST_PATH_STRONG_LOCK_HASH_PARTITIONS \
	(1 << FAST_PATH_STRONG_LOCK_HASH_BITS)
#define FastPathStrongLockHashPartition(hashcode) \
	((hashcode) % FAST_PATH_STRONG_LOCK_HASH_PARTITIONS)

typedef struct
{
	slock_t		mutex;
	uint32		count[FAST_PATH_STRONG_LOCK_HASH_PARTITIONS];
} FastPathStrongRelationLockData;

static volatile FastPathStrongRelationLockData *FastPathStrongRelationLocks;


/*
 * 指向包含锁状态的哈希表的指针
 *
 * LockMethodLockHash 和 LockMethodProcLockHash 哈希表在
 * 共享内存中；LockMethodLocalHash 对每个后端是本地的。
 */
static HTAB *LockMethodLockHash;
static HTAB *LockMethodProcLockHash;
static HTAB *LockMethodLocalHash;


/* 用于错误清理的私有状态 */
static LOCALLOCK *StrongLockInProgress;
static LOCALLOCK *awaitedLock;
static ResourceOwner awaitedOwner;


#ifdef LOCK_DEBUG

/*------
 * 以下配置选项可用于锁调试：
 *
 *	   TRACE_LOCKS		-- 输出此文件中发生的事情
 *	   TRACE_USERLOCKS	-- 相同，但针对用户锁
 *	   TRACE_LOCK_OIDMIN-- 不跟踪 OID 低于此的表的锁
 *						   （用于避免输出系统表）
 *	   TRACE_LOCK_TABLE -- 无条件跟踪该表（OID）的锁
 *	   DEBUG_DEADLOCKS	-- 当前在不合时宜的场合转储锁 ;)
 *
 * 此外，在 storage/lmgr/lwlock.c：
 *	   TRACE_LWLOCKS	-- 跟踪轻量级锁（相当无用）
 *
 * 在编译时定义 LOCK_DEBUG 以启用所有这些选项。
 * --------
 */

int			Trace_lock_oidmin = FirstNormalObjectId;
bool		Trace_locks = false;
bool		Trace_userlocks = false;
int			Trace_lock_table = 0;
bool		Debug_deadlocks = false;


inline static bool
fc_LOCK_DEBUG_ENABLED(const LOCKTAG *fc_tag)
{
	return
		(*(LockMethods[fc_tag->locktag_lockmethodid]->trace_flag) &&
		 ((Oid) fc_tag->locktag_field2 >= (Oid) Trace_lock_oidmin))
		|| (Trace_lock_table &&
			(fc_tag->locktag_field2 == Trace_lock_table));
}


inline static void fc_LOCK_PRINT(const char *fc_where, const LOCK *fc_lock, LOCKMODE fc_type)
{
	if (fc_LOCK_DEBUG_ENABLED(&fc_lock->tag))
		elog(LOG,
			 "%s: lock(%p) id(%u,%u,%u,%u,%u,%u) grantMask(%x) "
			 "req(%d,%d,%d,%d,%d,%d,%d)=%d "
			 "grant(%d,%d,%d,%d,%d,%d,%d)=%d wait(%d) type(%s)",
			 fc_where, fc_lock,
			 fc_lock->tag.locktag_field1, fc_lock->tag.locktag_field2,
			 fc_lock->tag.locktag_field3, fc_lock->tag.locktag_field4,
			 fc_lock->tag.locktag_type, fc_lock->tag.locktag_lockmethodid,
			 fc_lock->grantMask,
			 fc_lock->requested[1], fc_lock->requested[2], fc_lock->requested[3],
			 fc_lock->requested[4], fc_lock->requested[5], fc_lock->requested[6],
			 fc_lock->requested[7], fc_lock->nRequested,
			 fc_lock->granted[1], fc_lock->granted[2], fc_lock->granted[3],
			 fc_lock->granted[4], fc_lock->granted[5], fc_lock->granted[6],
			 fc_lock->granted[7], fc_lock->nGranted,
			 fc_lock->waitProcs.size,
			 LockMethods[LOCK_LOCKMETHOD(*fc_lock)]->lockModeNames[fc_type]);
}


inline static void fc_PROCLOCK_PRINT(const char *fc_where, const PROCLOCK *fc_proclockP)
{
	if (fc_LOCK_DEBUG_ENABLED(&fc_proclockP->tag.myLock->tag))
		elog(LOG,
			 "%s: proclock(%p) lock(%p) method(%u) proc(%p) hold(%x)",
			 fc_where, fc_proclockP, fc_proclockP->tag.myLock,
			 PROCLOCK_LOCKMETHOD(*(fc_proclockP)),
			 fc_proclockP->tag.myProc, (int) fc_proclockP->holdMask);
}
#else							/* 不是 LOCK_DEBUG */

#define fc_LOCK_PRINT(where, lock, type)  ((void) 0)
#define fc_PROCLOCK_PRINT(where, proclockP)  ((void) 0)
#endif							/* 不是 LOCK_DEBUG */


static uint32 fc_proclock_hash(const void *fc_key, Size fc_keysize);
static void fc_RemoveLocalLock(LOCALLOCK *fc_locallock);
static PROCLOCK *fc_SetupLockInTable(LockMethod fc_lockMethodTable, PGPROC *fc_proc,
								  const LOCKTAG *fc_locktag, uint32 fc_hashcode, LOCKMODE fc_lockmode);
static void fc_GrantLockLocal(LOCALLOCK *fc_locallock, ResourceOwner fc_owner);
static void fc_BeginStrongLockAcquire(LOCALLOCK *fc_locallock, uint32 fc_fasthashcode);
static void fc_FinishStrongLockAcquire(void);
static void fc_WaitOnLock(LOCALLOCK *fc_locallock, ResourceOwner fc_owner);
static void fc_ReleaseLockIfHeld(LOCALLOCK *fc_locallock, bool fc_sessionLock);
static void fc_LockReassignOwner(LOCALLOCK *fc_locallock, ResourceOwner fc_parent);
static bool fc_UnGrantLock(LOCK *fc_lock, LOCKMODE fc_lockmode,
						PROCLOCK *fc_proclock, LockMethod fc_lockMethodTable);
static void fc_CleanUpLock(LOCK *fc_lock, PROCLOCK *fc_proclock,
						LockMethod fc_lockMethodTable, uint32 fc_hashcode,
						bool fc_wakeupNeeded);
static void fc_LockRefindAndRelease(LockMethod fc_lockMethodTable, PGPROC *fc_proc,
								 LOCKTAG *fc_locktag, LOCKMODE fc_lockmode,
								 bool fc_decrement_strong_lock_count);
static void fc_GetSingleProcBlockerStatusData(PGPROC *fc_blocked_proc,
										   BlockedProcsData *fc_data);


/*
 * InitLocks -- 初始化锁管理器的数据结构。
 *
 * 这是从 CreateSharedMemoryAndSemaphores() 调用的，详见
 * 更多注释。在正常的主进程案例中，此处创建共享哈希表，
 * 以及一个在主进程中将保持未使用和空的局部锁哈希表。
 * 后端通过 fork() 继承指向共享表的指针，
 * 并且也继承局部锁哈希表的映像，随后继续使用它。
 * 在 EXEC_BACKEND 的情况下，每个后端重新执行此代码，
 * 以获得对已存在的共享哈希表的指针，并创建其局部锁哈希表。
 */
void InitLocks(void)
{
	HASHCTL		fc_info;
	long		fc_init_table_size,
				fc_max_table_size;
	bool		fc_found;

	/*
	 * 计算请求锁哈希表的初始化/最大大小。请注意，这些
	 * 计算必须与 LockShmemSize 一致！
	 */
	fc_max_table_size = NLOCKENTS();
	fc_init_table_size = fc_max_table_size / 2;

	/*
	 * 为 LOCK 结构分配哈希表。这存储每个锁定对象的信息。
	 */
	fc_info.keysize = sizeof(LOCKTAG);
	fc_info.entrysize = sizeof(LOCK);
	fc_info.num_partitions = NUM_LOCK_PARTITIONS;

	LockMethodLockHash = ShmemInitHash("LOCK hash",
									   fc_init_table_size,
									   fc_max_table_size,
									   &fc_info,
									   HASH_ELEM | HASH_BLOBS | HASH_PARTITION);

	/* 假设每个锁平均有 2 个持有者 */
	fc_max_table_size *= 2;
	fc_init_table_size *= 2;

	
/*
	 * 为PROCLOCK结构分配哈希表。 这存储
	 * 每个锁，每个持有者的信息。
	 */
	fc_info.keysize = sizeof(PROCLOCKTAG);
	fc_info.entrysize = sizeof(PROCLOCK);
	fc_info.hash = fc_proclock_hash;
	fc_info.num_partitions = NUM_LOCK_PARTITIONS;

	LockMethodProcLockHash = ShmemInitHash("PROCLOCK hash",
										   fc_init_table_size,
										   fc_max_table_size,
										   &fc_info,
										   HASH_ELEM | HASH_FUNCTION | HASH_PARTITION);

	/*
	 * 分配快速路径结构。
	 */
	FastPathStrongRelationLocks =
		ShmemInitStruct("Fast Path Strong Relation Lock Data",
						sizeof(FastPathStrongRelationLockData), &fc_found);
	if (!fc_found)
		SpinLockInit(&FastPathStrongRelationLocks->mutex);

	/*
	 * 为LOCALLOCK结构分配非共享哈希表。 这存储锁
	 * 计数和资源拥有者信息。
	 *
	 * 非共享表在这个进程中可能已经存在（当postmaster在后端崩溃后重建共享内存时会发生）。
	 * 如果是这样，删除并重新创建它。  （我们可以简单地将其保留，因为它
	 * 应该在postmaster中是空的，但出于安全考虑，让我们清除它。）
	 */
	if (LockMethodLocalHash)
		hash_destroy(LockMethodLocalHash);

	fc_info.keysize = sizeof(LOCALLOCKTAG);
	fc_info.entrysize = sizeof(LOCALLOCK);

	LockMethodLocalHash = hash_create("LOCALLOCK hash",
									  16,
									  &fc_info,
									  HASH_ELEM | HASH_BLOBS);
}


/*
 * 获取与给定锁相关联的锁方法表
 */
LockMethod
GetLocksMethodTable(const LOCK *fc_lock)
{
	LOCKMETHODID fc_lockmethodid = LOCK_LOCKMETHOD(*fc_lock);

	Assert(0 < fc_lockmethodid && fc_lockmethodid < lengthof(LockMethods));
	return LockMethods[fc_lockmethodid];
}

/*
 * 获取与给定锁标签相关联的锁方法表
 */
LockMethod
GetLockTagsMethodTable(const LOCKTAG *fc_locktag)
{
	LOCKMETHODID fc_lockmethodid = (LOCKMETHODID) fc_locktag->locktag_lockmethodid;

	Assert(0 < fc_lockmethodid && fc_lockmethodid < lengthof(LockMethods));
	return LockMethods[fc_lockmethodid];
}


/*
 * 计算与LOCKTAG相关联的哈希码。
 *
 * 为了避免不必要的哈希码重复计算，我们尝试在每个函数中
 * 仅执行一次，然后根据需要传递它。  除了
 * 将哈希码传递给hash_search_with_hash_value()，我们可以从哈希码中提取
 * 锁分区号。
 */
uint32 LockTagHashCode(const LOCKTAG *fc_locktag)
{
	return get_hash_value(LockMethodLockHash, (const void *) fc_locktag);
}

/*
 * 计算与PROCLOCKTAG相关联的哈希码。
 *
 * 因为我们只想为LOCK和PROCLOCK哈希表使用一组分区锁，
 * 所以我们必须确保PROCLOCK的
 * 分区号与其相关联的LOCK相同。
 * dynahash.c期望分区号是
 * 哈希码的低位，因此PROCLOCKTAG的哈希码必须具有与相关LOCKTAG的哈希码相同的低位。我们通过
 * 这个专门的哈希函数来实现这一点。
 */
static uint32 fc_proclock_hash(const void *fc_key, Size fc_keysize)
{
	const PROCLOCKTAG *fc_proclocktag = (const PROCLOCKTAG *) fc_key;
	uint32		fc_lockhash;
	Datum		fc_procptr;

	Assert(fc_keysize == sizeof(PROCLOCKTAG));

	/* 查看相关的LOCK对象，计算其哈希码 */
	fc_lockhash = LockTagHashCode(&fc_proclocktag->myLock->tag);

	/*
	 * 为了使哈希码也依赖于PGPROC，我们将proc
	 * 结构的地址进行异或运算，左移以确保
	 * 分区号位不变。 由于这只是一个哈希，我们
	 * 不在乎是否丢失地址的高位；使用一个
	 * 中间变量来抑制指针转换为整数的警告。
	 */
	fc_procptr = PointerGetDatum(fc_proclocktag->myProc);
	fc_lockhash ^= ((uint32) fc_procptr) << LOG2_NUM_LOCK_PARTITIONS;

	return fc_lockhash;
}

/*
 * 计算与PROCLOCKTAG关联的哈希码，给定其基础LOCK的哈希码。
 *
 * 我们这样做只是为了避免对LockTagHashCode()的重复调用。
 */
static inline uint32 fc_ProcLockHashCode(const PROCLOCKTAG *fc_proclocktag, uint32 fc_hashcode)
{
	uint32		fc_lockhash = fc_hashcode;
	Datum		fc_procptr;

	/*
	 * 这必须与proclock_hash()匹配！
	 */
	fc_procptr = PointerGetDatum(fc_proclocktag->myProc);
	fc_lockhash ^= ((uint32) fc_procptr) << LOG2_NUM_LOCK_PARTITIONS;

	return fc_lockhash;
}

/*
 * 给定两种锁模式，返回它们是否会冲突。
 */
bool DoLockModesConflict(LOCKMODE fc_mode1, LOCKMODE fc_mode2)
{
	LockMethod	fc_lockMethodTable = LockMethods[DEFAULT_LOCKMETHOD];

	if (fc_lockMethodTable->conflictTab[fc_mode1] & LOCKBIT_ON(fc_mode2))
		return true;

	return false;
}

/*
 * LockHeldByMeExtended -- 测试当前事务是否持有锁'locktag'。
 *
 * 如果当前事务持有'锁标签'的'锁模式'上的锁，则返回true。
 * 如果'orstronger'为true，则更强的锁模式也可以。
 * （“更强”的定义为“数值更高”，这在语义上有点可疑，但对于我们使用的目的来说是可以的。）
 */
static bool fc_LockHeldByMeExtended(const LOCKTAG *fc_locktag,
					 LOCKMODE fc_lockmode, bool fc_orstronger)
{
	LOCALLOCKTAG fc_localtag;
	LOCALLOCK  *fc_locallock;

	/*
	 * 查看该锁和锁模式是否存在LOCALLOCK条目
	 */
	MemSet(&fc_localtag, 0, sizeof(fc_localtag)); /* 必须清除填充 */
	fc_localtag.lock = *fc_locktag;
	fc_localtag.mode = fc_lockmode;

	fc_locallock = (LOCALLOCK *) hash_search(LockMethodLocalHash,
										  (void *) &fc_localtag,
										  HASH_FIND, NULL);

	if (fc_locallock && fc_locallock->nLocks > 0)
		return true;

	if (fc_orstronger)
	{
		LOCKMODE	fc_slockmode;

		for (fc_slockmode = fc_lockmode + 1;
			 fc_slockmode <= MaxLockMode;
			 fc_slockmode++)
		{
			if (fc_LockHeldByMeExtended(fc_locktag, fc_slockmode, false))
				return true;
		}
	}

	return false;
}

bool LockHeldByMe(const LOCKTAG *fc_locktag, LOCKMODE fc_lockmode)
{
	return fc_LockHeldByMeExtended(fc_locktag, fc_lockmode, false);
}

bool LockOrStrongerHeldByMe(const LOCKTAG *fc_locktag, LOCKMODE fc_lockmode)
{
	return fc_LockHeldByMeExtended(fc_locktag, fc_lockmode, true);
}

#ifdef USE_ASSERT_CHECKING
/*
 * GetLockMethodLocalHash -- 返回本地锁的哈希，用于根据所有持有的锁评估断言的模块。
 */
HTAB * GetLockMethodLocalHash(void)
{
	return LockMethodLocalHash;
}
#endif

/*
 * LockHasWaiters -- 查找'locktag'并检查释放此
 *	锁是否会唤醒其他等待它的进程。
 */
bool LockHasWaiters(const LOCKTAG *fc_locktag, LOCKMODE fc_lockmode, bool fc_sessionLock)
{
	LOCKMETHODID fc_lockmethodid = fc_locktag->locktag_lockmethodid;
	LockMethod	fc_lockMethodTable;
	LOCALLOCKTAG fc_localtag;
	LOCALLOCK  *fc_locallock;
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	LWLock	   *fc_partitionLock;
	bool		fc_hasWaiters = false;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);
	fc_lockMethodTable = LockMethods[fc_lockmethodid];
	if (fc_lockmode <= 0 || fc_lockmode > fc_lockMethodTable->numLockModes)
		elog(ERROR, "unrecognized lock mode: %d", fc_lockmode);

#ifdef LOCK_DEBUG
	if (fc_LOCK_DEBUG_ENABLED(fc_locktag))
		elog(LOG, "LockHasWaiters: lock [%u,%u] %s",
			 fc_locktag->locktag_field1, fc_locktag->locktag_field2,
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
#endif

	/*
	 * 查找此锁和锁模式的LOCALLOCK条目
	 */
	MemSet(&fc_localtag, 0, sizeof(fc_localtag)); /* 必须清除填充 */
	fc_localtag.lock = *fc_locktag;
	fc_localtag.mode = fc_lockmode;

	fc_locallock = (LOCALLOCK *) hash_search(LockMethodLocalHash,
										  (void *) &fc_localtag,
										  HASH_FIND, NULL);

	/*
	 * 让调用者自己打印错误消息，也可以。不要ereport(ERROR)。
	 */
	if (!fc_locallock || fc_locallock->nLocks <= 0)
	{
		elog(WARNING, "you don't own a lock of type %s",
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
		return false;
	}

	/*
	 * 检查共享锁表。
	 */
	fc_partitionLock = LockHashPartitionLock(fc_locallock->hashcode);

	LWLockAcquire(fc_partitionLock, LW_SHARED);

	/*
	 * 我们不需要重新查找锁或proclock，因为我们在locallock表中保存了它们的
	 * 地址，并且在我们持有锁的情况下，它们不可能被移除。
	 */
	fc_lock = fc_locallock->lock;
	fc_LOCK_PRINT("LockHasWaiters: found", fc_lock, fc_lockmode);
	fc_proclock = fc_locallock->proclock;
	fc_PROCLOCK_PRINT("LockHasWaiters: found", fc_proclock);

	/*
	 * 再次检查我们实际上是否持有我们想要释放的锁的类型。
	 */
	if (!(fc_proclock->holdMask & LOCKBIT_ON(fc_lockmode)))
	{
		fc_PROCLOCK_PRINT("LockHasWaiters: WRONGTYPE", fc_proclock);
		LWLockRelease(fc_partitionLock);
		elog(WARNING, "you don't own a lock of type %s",
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
		fc_RemoveLocalLock(fc_locallock);
		return false;
	}

	/*
	 * 进行检查。
	 */
	if ((fc_lockMethodTable->conflictTab[fc_lockmode] & fc_lock->waitMask) != 0)
		fc_hasWaiters = true;

	LWLockRelease(fc_partitionLock);

	return fc_hasWaiters;
}

/*
 * LockAcquire -- 检查锁冲突，如果发现冲突则休眠，
 *	在没有冲突时设置锁。
 *
 * 输入：
 *	locktag：可锁对象的唯一标识符
 *	lockmode：要获取的锁模式
 *	sessionLock：如果为true，则获取会话的锁而不是当前事务的锁
 *	dontWait：如果为true，则不等待获取锁
 *
 * 返回值之一：
 *		LOCKACQUIRE_NOT_AVAIL	锁不可用，且dontWait=true
 *		LOCKACQUIRE_OK			锁成功获取
 *		LOCKACQUIRE_ALREADY_HELD	已持有锁的计数增加
 *		LOCKACQUIRE_ALREADY_CLEAR	已清除锁的计数增加
 *
 * 在正常情况下，其中dontWait=false，并且调用者不需要
 * 将新获得的锁与此同一事务中以前已获取的锁区分开来，
 * 因此无需检查返回值。
 *
 * 副作用：锁被获取并记录在锁表中。
 *
 * 注意：如果我们等待锁，则没有其他方法可以中止等待，
 * 除非中止事务。
 */
LockAcquireResult
LockAcquire(const LOCKTAG *fc_locktag,
			LOCKMODE fc_lockmode,
			bool fc_sessionLock,
			bool fc_dontWait)
{
	return LockAcquireExtended(fc_locktag, fc_lockmode, fc_sessionLock, fc_dontWait,
							   true, NULL);
}

/*
 * LockAcquireExtended - 允许我们指定额外选项
 *
 * reportMemoryError指定锁请求填满锁表是否应生成错误。
 * 传递“false”允许调用者尝试从锁表已满的情况中恢复，
 * 可能通过强制取消其他锁持有者，然后重试。
 * 但是，请注意，返回代码为LOCKACQUIRE_NOT_AVAIL，
 * 因此在与dontWait = true结合使用时不安全，因为无法
 * 区分故障的原因。
 *
 * 如果locallockp不为NULL，*locallockp在成功获取锁时接收
 * 指向LOCALLOCK表条目的指针，如果未成功则为NULL。
 */
LockAcquireResult
LockAcquireExtended(const LOCKTAG *fc_locktag,
					LOCKMODE fc_lockmode,
					bool fc_sessionLock,
					bool fc_dontWait,
					bool fc_reportMemoryError,
					LOCALLOCK **fc_locallockp)
{
	LOCKMETHODID fc_lockmethodid = fc_locktag->locktag_lockmethodid;
	LockMethod	fc_lockMethodTable;
	LOCALLOCKTAG fc_localtag;
	LOCALLOCK  *fc_locallock;
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	bool		fc_found;
	ResourceOwner fc_owner;
	uint32		fc_hashcode;
	LWLock	   *fc_partitionLock;
	bool		fc_found_conflict;
	bool		fc_log_lock = false;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);
	fc_lockMethodTable = LockMethods[fc_lockmethodid];
	if (fc_lockmode <= 0 || fc_lockmode > fc_lockMethodTable->numLockModes)
		elog(ERROR, "unrecognized lock mode: %d", fc_lockmode);

	if (RecoveryInProgress() && !InRecovery &&
		(fc_locktag->locktag_type == LOCKTAG_OBJECT ||
		 fc_locktag->locktag_type == LOCKTAG_RELATION) &&
		fc_lockmode > RowExclusiveLock)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("cannot acquire lock mode %s on database objects while recovery is in progress",
						fc_lockMethodTable->lockModeNames[fc_lockmode]),
				 errhint("Only RowExclusiveLock or less can be acquired on database objects during recovery.")));

#ifdef LOCK_DEBUG
	if (fc_LOCK_DEBUG_ENABLED(fc_locktag))
		elog(LOG, "LockAcquire: lock [%u,%u] %s",
			 fc_locktag->locktag_field1, fc_locktag->locktag_field2,
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
#endif

	/* 识别锁的所有者 */
	if (fc_sessionLock)
		fc_owner = NULL;
	else
		fc_owner = CurrentResourceOwner;

	/*
	 * 查找或创建此锁和锁模式的LOCALLOCK条目
	 */
	MemSet(&fc_localtag, 0, sizeof(fc_localtag)); /* 必须清除填充 */
	fc_localtag.lock = *fc_locktag;
	fc_localtag.mode = fc_lockmode;

	fc_locallock = (LOCALLOCK *) hash_search(LockMethodLocalHash,
										  (void *) &fc_localtag,
										  HASH_ENTER, &fc_found);

	/*
	 * 如果这是一个新的locallock对象，则初始化它
	 */
	if (!fc_found)
	{
		fc_locallock->lock = NULL;
		fc_locallock->proclock = NULL;
		fc_locallock->hashcode = LockTagHashCode(&(fc_localtag.lock));
		fc_locallock->nLocks = 0;
		fc_locallock->holdsStrongLockCount = false;
		fc_locallock->lockCleared = false;
		fc_locallock->numLockOwners = 0;
		fc_locallock->maxLockOwners = 8;
		fc_locallock->lockOwners = NULL;	/* 以防下一行失败 */
		fc_locallock->lockOwners = (LOCALLOCKOWNER *)
			MemoryContextAlloc(TopMemoryContext,
							   fc_locallock->maxLockOwners * sizeof(LOCALLOCKOWNER));
	}
	else
	{
		/* 确保有空间记住锁 */
		if (fc_locallock->numLockOwners >= fc_locallock->maxLockOwners)
		{
			int			fc_newsize = fc_locallock->maxLockOwners * 2;

			fc_locallock->lockOwners = (LOCALLOCKOWNER *)
				repalloc(fc_locallock->lockOwners,
						 fc_newsize * sizeof(LOCALLOCKOWNER));
			fc_locallock->maxLockOwners = fc_newsize;
		}
	}
	fc_hashcode = fc_locallock->hashcode;

	if (fc_locallockp)
		*fc_locallockp = fc_locallock;

	/*
	 * 如果我们已经持有锁，我们可以只是在本地增加计数。
	 *
	 * 如果lockCleared已设置，调用者无需担心吸收与锁对象
	 * 相关的sinval消息。
	 */
	if (fc_locallock->nLocks > 0)
	{
		fc_GrantLockLocal(fc_locallock, fc_owner);
		if (fc_locallock->lockCleared)
			return LOCKACQUIRE_ALREADY_CLEAR;
		else
			return LOCKACQUIRE_ALREADY_HELD;
	}

	/*
	 * 在持有关系扩展锁的情况下，我们不会获取其他重量级锁。
	 * 我们确实允许多次获取同一关系扩展锁，但该情况不会到达这里。
	 */
	Assert(!IsRelationExtensionLockHeld);

	/*
	 * 准备在需要在备用服务器中重放获取此锁的WAL记录。
	 *
	 * 在这里，我们准备记录；在获取锁之后，我们将发出日志记录。
	 * 这种安排简化了在准备步骤失败时的错误恢复。
	 *
	 * 仅AccessExclusiveLocks可以与只读事务可以在备用服务器中获取的锁类型冲突。
	 * 确保此定义与GetRunningTransactionLocks()中的定义匹配。
	 */
	if (fc_lockmode >= AccessExclusiveLock &&
		fc_locktag->locktag_type == LOCKTAG_RELATION &&
		!RecoveryInProgress() &&
		XLogStandbyInfoActive())
	{
		LogAccessExclusiveLockPrepare();
		fc_log_lock = true;
	}

	
/*
	 * 尝试通过快速路径获取锁，如果符合条件。但是如果我们记得
	 * 快速路径数组已经满了，我们就不再尝试进一步使用它，直到我们释放一些锁。
	 * 可能其他后端已经将其中一些锁转移到共享哈希表中，腾出了空间，
	 * 但为了检查这一点，获得LWLock是没有必要的。也可能我们
	 * 正在获取一个在我们已经使用快速路径锁定的关系上的第二个或第三个
	 * 锁类型，但现在我们也不担心这种情况。
	 */
	if (EligibleForRelationFastPath(fc_locktag, fc_lockmode) &&
		FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND)
	{
		uint32		fc_fasthashcode = FastPathStrongLockHashPartition(fc_hashcode);
		bool		fc_acquired;

		/*
		 * LWLockAcquire作为一个内存序列点，因此可以安全地假设任何强锁定在我们
		 * 测试后其对FastPathStrongRelationLocks->counts的增量变得可见时，
		 * 还未开始转移快速路径锁。
		 */
		LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);
		if (FastPathStrongRelationLocks->count[fc_fasthashcode] != 0)
			fc_acquired = false;
		else
			fc_acquired = fc_FastPathGrantRelationLock(fc_locktag->locktag_field2,
												 fc_lockmode);
		LWLockRelease(&MyProc->fpInfoLock);
		if (fc_acquired)
		{
			/*
			 * locallock可能包含对一些旧共享对象的陈旧指针；
			 * 我们必须在考虑通过快速路径获取锁之前将这些重置为null。
			 */
			fc_locallock->lock = NULL;
			fc_locallock->proclock = NULL;
			fc_GrantLockLocal(fc_locallock, fc_owner);
			return LOCKACQUIRE_OK;
		}
	}

	/*
	 * 如果这个锁可能已经被其他后端通过快速路径获取，
	 * 我们必须（暂时）禁用对该锁标签的进一步快速路径使用，
	 * 并将通过这种方法已获取的任何锁迁移到主锁表。
	 */
	if (ConflictsWithRelationFastPath(fc_locktag, fc_lockmode))
	{
		uint32		fc_fasthashcode = FastPathStrongLockHashPartition(fc_hashcode);

		fc_BeginStrongLockAcquire(fc_locallock, fc_fasthashcode);
		if (!fc_FastPathTransferRelationLocks(fc_lockMethodTable, fc_locktag,
										   fc_hashcode))
		{
			AbortStrongLockAcquire();
			if (fc_locallock->nLocks == 0)
				fc_RemoveLocalLock(fc_locallock);
			if (fc_locallockp)
				*fc_locallockp = NULL;
			if (fc_reportMemoryError)
				ereport(ERROR,
						(errcode(ERRCODE_OUT_OF_MEMORY),
						 errmsg("out of shared memory"),
						 errhint("You might need to increase max_locks_per_transaction.")));
#ifndef FDD //cppcheck,FDD 删除'else'，然后无论是否报告错误都返回。
			else
#endif
				return LOCKACQUIRE_NOT_AVAIL;
		}
	}

	/*
	 * 我们没有在 LOCALLOCK 表中找到锁，也没有通过快速路径获取锁，所以我们必须处理共享锁表。
	 */
	fc_partitionLock = LockHashPartitionLock(fc_hashcode);

	LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

	/*
	 * 查找或创建带有此标签的锁和 proclock 条目
	 *
	 * 注意：如果 locallock 对象已经存在，可能已经有指向该锁的指针
	 * ……但我们不应假设该指针是有效的，因为持有和请求计数为零的锁对象
	 * 随时可能消失。因此，即使它们已经设置，我们也必须使用 SetupLockInTable()
	 * 重新计算锁和 proclock 指针。
	 */
	fc_proclock = fc_SetupLockInTable(fc_lockMethodTable, MyProc, fc_locktag,
								fc_hashcode, fc_lockmode);
	if (!fc_proclock)
	{
		AbortStrongLockAcquire();
		LWLockRelease(fc_partitionLock);
		if (fc_locallock->nLocks == 0)
			fc_RemoveLocalLock(fc_locallock);
		if (fc_locallockp)
			*fc_locallockp = NULL;
		if (fc_reportMemoryError)
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of shared memory"),
					 errhint("You might need to increase max_locks_per_transaction.")));
#ifndef FDD //cppcheck,FDD 删除'else'，然后无论是否报告错误都返回。
		else
#endif
			return LOCKACQUIRE_NOT_AVAIL;
	}
	fc_locallock->proclock = fc_proclock;
	fc_lock = fc_proclock->tag.myLock;
	fc_locallock->lock = fc_lock;

	/*
	 * 如果请求的锁与等待者请求的锁冲突，必须加入等待队列。
	 * 否则，检查是否与已持有的锁冲突。
	 * （这是最后一步，因为它是最复杂的检查。）
	 */
	if (fc_lockMethodTable->conflictTab[fc_lockmode] & fc_lock->waitMask)
		fc_found_conflict = true;
	else
		fc_found_conflict = LockCheckConflicts(fc_lockMethodTable, fc_lockmode,
											fc_lock, fc_proclock);

	if (!fc_found_conflict)
	{
		/* 与已持有或以前请求的锁没有冲突 */
		GrantLock(fc_lock, fc_proclock, fc_lockmode);
		fc_GrantLockLocal(fc_locallock, fc_owner);
	}
	else
	{
		/*
		 * 我们不能立即获取锁。如果调用者指定不阻塞，移除
		 * 无用的表条目并返回 LOCKACQUIRE_NOT_AVAIL 而不等待。
		 */
		if (fc_dontWait)
		{
			AbortStrongLockAcquire();
			if (fc_proclock->holdMask == 0)
			{
				uint32		fc_proclock_hashcode;

				fc_proclock_hashcode = fc_ProcLockHashCode(&fc_proclock->tag, fc_hashcode);
				SHMQueueDelete(&fc_proclock->lockLink);
				SHMQueueDelete(&fc_proclock->procLink);
				if (!hash_search_with_hash_value(LockMethodProcLockHash,
												 (void *) &(fc_proclock->tag),
												 fc_proclock_hashcode,
												 HASH_REMOVE,
												 NULL))
					elog(PANIC, "proclock table corrupted");
			}
			else
				fc_PROCLOCK_PRINT("LockAcquire: NOWAIT", fc_proclock);
			fc_lock->nRequested--;
			fc_lock->requested[fc_lockmode]--;
			fc_LOCK_PRINT("LockAcquire: conditional lock failed", fc_lock, fc_lockmode);
			Assert((fc_lock->nRequested > 0) && (fc_lock->requested[fc_lockmode] >= 0));
			Assert(fc_lock->nGranted <= fc_lock->nRequested);
			LWLockRelease(fc_partitionLock);
			if (fc_locallock->nLocks == 0)
				fc_RemoveLocalLock(fc_locallock);
			if (fc_locallockp)
				*fc_locallockp = NULL;
			return LOCKACQUIRE_NOT_AVAIL;
		}

		/*
		 * 设置此进程在该对象上已经持有的锁的位掩码。
		 */
		MyProc->heldLocks = fc_proclock->holdMask;

		/*
		 * 睡觉，直到有人把我叫醒。
		 */

		TRACE_POSTGRESQL_LOCK_WAIT_START(fc_locktag->locktag_field1,
										 fc_locktag->locktag_field2,
										 fc_locktag->locktag_field3,
										 fc_locktag->locktag_field4,
										 fc_locktag->locktag_type,
										 fc_lockmode);

		fc_WaitOnLock(fc_locallock, fc_owner);

		TRACE_POSTGRESQL_LOCK_WAIT_DONE(fc_locktag->locktag_field1,
										fc_locktag->locktag_field2,
										fc_locktag->locktag_field3,
										fc_locktag->locktag_field4,
										fc_locktag->locktag_type,
										fc_lockmode);

		/*
		 * 注意：在这里和返回之间不要进行任何实际的状态变化。
		 * 在授予我们锁时，锁表状态的所有必要更改都必须完成 --- 请参阅 WaitOnLock 中的说明。
		 */

		/*
		 * 检查 proclock 条目状态，以防 ipc 通信中出现任何问题。
		 */
		if (!(fc_proclock->holdMask & LOCKBIT_ON(fc_lockmode)))
		{
			AbortStrongLockAcquire();
			fc_PROCLOCK_PRINT("LockAcquire: INCONSISTENT", fc_proclock);
			fc_LOCK_PRINT("LockAcquire: INCONSISTENT", fc_lock, fc_lockmode);
			/* 我们应该重试吗？ */
			LWLockRelease(fc_partitionLock);
			elog(ERROR, "LockAcquire failed");
		}
		fc_PROCLOCK_PRINT("LockAcquire: granted", fc_proclock);
		fc_LOCK_PRINT("LockAcquire: granted", fc_lock, fc_lockmode);
	}

	/*
	 * 锁状态现在是完全最新的；如果我们在此之后出错，不需要特殊的错误清理。
	 */
	fc_FinishStrongLockAcquire();

	LWLockRelease(fc_partitionLock);

	/*
	 * 如果在备用服务器中需要重新播放该锁的获取，
	 * 则发出 WAL 记录。
	 */
	if (fc_log_lock)
	{
		/*
		 * 解码锁标签回原始值，以避免在每个消息中发送
		 * 大量空字节。请参见 lock.h 以检查 LOCKTAG_RELATION 的锁标签是如何定义的。
		 */
		LogAccessExclusiveLock(fc_locktag->locktag_field1,
							   fc_locktag->locktag_field2);
	}

	return LOCKACQUIRE_OK;
}

/*
 * 根据新锁请求的需要查找或创建 LOCK 和 PROCLOCK 对象。
 *
 * 如果由于缺乏共享内存而无法创建对象，则返回 PROCLOCK 对象，或返回 NULL。
 *
 * 入口时必须持有适当的分区锁，并将在退出时持有。
 */
static PROCLOCK *
fc_SetupLockInTable(LockMethod fc_lockMethodTable, PGPROC *fc_proc,
				 const LOCKTAG *fc_locktag, uint32 fc_hashcode, LOCKMODE fc_lockmode)
{
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	PROCLOCKTAG fc_proclocktag;
	uint32		fc_proclock_hashcode;
	bool		fc_found;

	/*
	 * 查找或创建带有此标签的锁。
	 */
	fc_lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
												(const void *) fc_locktag,
												fc_hashcode,
												HASH_ENTER_NULL,
												&fc_found);
	if (!fc_lock)
		return NULL;

	/*
	 * 如果是新的锁对象，则初始化它。
	 */
	if (!fc_found)
	{
		fc_lock->grantMask = 0;
		fc_lock->waitMask = 0;
		SHMQueueInit(&(fc_lock->procLocks));
		ProcQueueInit(&(fc_lock->waitProcs));
		fc_lock->nRequested = 0;
		fc_lock->nGranted = 0;
		MemSet(fc_lock->requested, 0, sizeof(int) * MAX_LOCKMODES);
		MemSet(fc_lock->granted, 0, sizeof(int) * MAX_LOCKMODES);
		fc_LOCK_PRINT("LockAcquire: new", fc_lock, fc_lockmode);
	}
	else
	{
		fc_LOCK_PRINT("LockAcquire: found", fc_lock, fc_lockmode);
		Assert((fc_lock->nRequested >= 0) && (fc_lock->requested[fc_lockmode] >= 0));
		Assert((fc_lock->nGranted >= 0) && (fc_lock->granted[fc_lockmode] >= 0));
		Assert(fc_lock->nGranted <= fc_lock->nRequested);
	}

	/*
	 * 为 proclock 表创建哈希键。
	 */
	fc_proclocktag.myLock = fc_lock;
	fc_proclocktag.myProc = fc_proc;

	fc_proclock_hashcode = fc_ProcLockHashCode(&fc_proclocktag, fc_hashcode);

	/*
	 * 查找或创建带有此标签的 proclock 条目。
	 */
	fc_proclock = (PROCLOCK *) hash_search_with_hash_value(LockMethodProcLockHash,
														(void *) &fc_proclocktag,
														fc_proclock_hashcode,
														HASH_ENTER_NULL,
														&fc_found);
	if (!fc_proclock)
	{
		/* 哎呀，proclock 的共享内存不够 */
		if (fc_lock->nRequested == 0)
		{
			/*
			 * 该锁没有其他请求者，因此需要进行垃圾回收
			 * 锁对象。我们*必须*这样做以避免共享内存的永久泄漏，
			 * 因为没有任何东西可以导致后续释放锁对象。
			 */
			Assert(SHMQueueEmpty(&(fc_lock->procLocks)));
			if (!hash_search_with_hash_value(LockMethodLockHash,
											 (void *) &(fc_lock->tag),
											 fc_hashcode,
											 HASH_REMOVE,
											 NULL))
				elog(PANIC, "lock table corrupted");
		}
		return NULL;
	}

	/*
	 * 如果是新的，初始化新的条目。
	 */
	if (!fc_found)
	{
		uint32		fc_partition = LockHashPartition(fc_hashcode);

		/*
		 * 访问 proclock->groupLeader 而不加锁似乎不安全，但实际上并不是。
		 * 我们要么是在为自己初始化 proclock，在这种情况下我们的组领导者没有变化，
		 * 因为进程的组领导者只能由进程本身更改；要么我们将快速路径锁转移到
		 * 主锁表，在这种情况下该进程在释放所有锁之前不能更改其锁组领导者（尤其是我们
		 * 当前正在转移的那个锁）。
		 */
		fc_proclock->groupLeader = fc_proc->lockGroupLeader != NULL ?
			fc_proc->lockGroupLeader : fc_proc;
		fc_proclock->holdMask = 0;
		fc_proclock->releaseMask = 0;
		/* 将 proclock 添加到适当的列表 */
		SHMQueueInsertBefore(&fc_lock->procLocks, &fc_proclock->lockLink);
		SHMQueueInsertBefore(&(fc_proc->myProcLocks[fc_partition]),
							 &fc_proclock->procLink);
		fc_PROCLOCK_PRINT("LockAcquire: new", fc_proclock);
	}
	else
	{
		fc_PROCLOCK_PRINT("LockAcquire: found", fc_proclock);
		Assert((fc_proclock->holdMask & ~fc_lock->grantMask) == 0);

#ifdef CHECK_DEADLOCK_RISK

		/*
		 * 如果我们已经在该对象上持有较低级别的锁，并且没有持有请求级别或更高级别的锁，则发出警告。
		 * 这表明一种易导致死锁的编码实践（例如，如果另一个后端
		 * 在大约相同的时间遵循相同的代码路径，我们就会发生死锁）。
		 *
		 * 这在默认情况下未启用，因为它可能会生成
		 * 关于用户级编码实践的日志条目，而这些实践在上下文中实际上是安全的。
		 * 它可以启用以帮助发现系统级问题。
		 *
		 * XXX 在锁模式上进行数值比较是一种变通办法；使用表会更好。
		 * 不过，对于现在来说，这样有效。
		 */
		{
			int			fc_i;

			for (fc_i = fc_lockMethodTable->numLockModes; fc_i > 0; fc_i--)
			{
				if (fc_proclock->holdMask & LOCKBIT_ON(fc_i))
				{
					if (fc_i >= (int) fc_lockmode)
						break;	/* 安全：我们有一个锁 >= 请求级别 */
					elog(LOG, "deadlock risk: raising lock level"
						 " from %s to %s on object %u/%u/%u",
						 fc_lockMethodTable->lockModeNames[fc_i],
						 fc_lockMethodTable->lockModeNames[fc_lockmode],
						 fc_lock->tag.locktag_field1, fc_lock->tag.locktag_field2,
						 fc_lock->tag.locktag_field3);
					break;
				}
			}
		}
#endif							/* CHECK_DEADLOCK_RISK */
	}

	/*
	 * lock->nRequested 和 lock->requested[] 计数所有请求的总数，
	 * 无论是被授予的还是等待的，因此立即增加它们。
	 * 其他计数在我们获取锁之前不会增加。
	 */
	fc_lock->nRequested++;
	fc_lock->requested[fc_lockmode]++;
	Assert((fc_lock->nRequested > 0) && (fc_lock->requested[fc_lockmode] > 0));

	/*
	 * 我们不应该已经持有所需的锁；否则 locallock 表会损坏。
	 */
	if (fc_proclock->holdMask & LOCKBIT_ON(fc_lockmode))
		elog(ERROR, "lock %s on object %u/%u/%u is already held",
			 fc_lockMethodTable->lockModeNames[fc_lockmode],
			 fc_lock->tag.locktag_field1, fc_lock->tag.locktag_field2,
			 fc_lock->tag.locktag_field3);

	return fc_proclock;
}

/*
 * 检查并设置/重置我们持有的关系扩展锁的标志。
 *
 * 调用者有责任在获得/释放关系扩展锁后调用此函数。
 *
 * 如果锁已获得，则传入true；否则传入false。
 */
static inline void fc_CheckAndSetLockHeld(LOCALLOCK *fc_locallock, bool fc_acquired)
{
#ifdef USE_ASSERT_CHECKING
	if (LOCALLOCK_LOCKTAG(*fc_locallock) == LOCKTAG_RELATION_EXTEND)
		IsRelationExtensionLockHeld = fc_acquired;
#endif
}

/*
 * 释放locallock条目的子例程
 */
static void fc_RemoveLocalLock(LOCALLOCK *fc_locallock)
{
	int			fc_i;

	for (fc_i = fc_locallock->numLockOwners - 1; fc_i >= 0; fc_i--)
	{
		if (fc_locallock->lockOwners[fc_i].owner != NULL)
			ResourceOwnerForgetLock(fc_locallock->lockOwners[fc_i].owner, fc_locallock);
	}
	fc_locallock->numLockOwners = 0;
	if (fc_locallock->lockOwners != NULL)
		pfree(fc_locallock->lockOwners);
	fc_locallock->lockOwners = NULL;

	if (fc_locallock->holdsStrongLockCount)
	{
		uint32		fc_fasthashcode;

		fc_fasthashcode = FastPathStrongLockHashPartition(fc_locallock->hashcode);

		SpinLockAcquire(&FastPathStrongRelationLocks->mutex);
		Assert(FastPathStrongRelationLocks->count[fc_fasthashcode] > 0);
		FastPathStrongRelationLocks->count[fc_fasthashcode]--;
		fc_locallock->holdsStrongLockCount = false;
		SpinLockRelease(&FastPathStrongRelationLocks->mutex);
	}

	if (!hash_search(LockMethodLocalHash,
					 (void *) &(fc_locallock->tag),
					 HASH_REMOVE, NULL))
		elog(WARNING, "locallock table corrupted");

	/*
	 * 指示某些类型的锁已释放
	 */
	fc_CheckAndSetLockHeld(fc_locallock, false);
}

/*
 * LockCheckConflicts -- 测试请求的锁是否与已授予的锁冲突
 *
 * 如果冲突返回true，如果没有冲突返回false。
 *
 * 注：
 *		这使事情变得复杂的原因是：一个进程的锁不会相互冲突，无论它们出于何种目的持有
 * （例如，会话锁和事务锁没有冲突）。同一锁组中一个进程的锁与另一个进程的锁也不会冲突。
 * 因此，在确定请求的新锁是否与已持有的锁冲突时，必须减去这些锁。
 */
bool LockCheckConflicts(LockMethod fc_lockMethodTable,
				   LOCKMODE fc_lockmode,
				   LOCK *fc_lock,
				   PROCLOCK *fc_proclock)
{
	int			fc_numLockModes = fc_lockMethodTable->numLockModes;
	LOCKMASK	fc_myLocks;
	int			fc_conflictMask = fc_lockMethodTable->conflictTab[fc_lockmode];
	int			fc_conflictsRemaining[MAX_LOCKMODES];
	int			fc_totalConflictsRemaining = 0;
	int			fc_i;
	SHM_QUEUE  *fc_procLocks;
	PROCLOCK   *fc_otherproclock;

	/*
	 * 首先检查全局冲突：如果没有锁与我的请求冲突，
	 * 那么我就获得锁。
	 *
	 * 检查冲突：lock->grantMask代表当前持有锁的类型。 
	 * conflictTable[lockmode] 为与请求冲突的每种锁类型设置一个位。
	 * 按位比较可以确定是否存在冲突。
	 */
	if (!(fc_conflictMask & fc_lock->grantMask))
	{
		fc_PROCLOCK_PRINT("LockCheckConflicts: no conflict", fc_proclock);
		return false;
	}

	/*
	 * 糟糕。有些冲突。但是它仍然可能是我自己的锁，或者是我锁定组中的其他成员持有的锁。
	 * 首先，计算在减去我自己持有的任何锁后，剩余的冲突数量。
	 */
	fc_myLocks = fc_proclock->holdMask;
	for (fc_i = 1; fc_i <= fc_numLockModes; fc_i++)
	{
		if ((fc_conflictMask & LOCKBIT_ON(fc_i)) == 0)
		{
			fc_conflictsRemaining[fc_i] = 0;
			continue;
		}
		fc_conflictsRemaining[fc_i] = fc_lock->granted[fc_i];
		if (fc_myLocks & LOCKBIT_ON(fc_i))
			--fc_conflictsRemaining[fc_i];
		fc_totalConflictsRemaining += fc_conflictsRemaining[fc_i];
	}

	/* 如果没有冲突，获取锁。 */
	if (fc_totalConflictsRemaining == 0)
	{
		fc_PROCLOCK_PRINT("LockCheckConflicts: resolved (simple)", fc_proclock);
		return false;
	}

	/* 如果没有组锁，则肯定存在冲突。 */
	if (fc_proclock->groupLeader == MyProc && MyProc->lockGroupLeader == NULL)
	{
		Assert(fc_proclock->tag.myProc == MyProc);
		fc_PROCLOCK_PRINT("LockCheckConflicts: conflicting (simple)",
					   fc_proclock);
		return true;
	}

	/*
	 * 关系扩展锁的冲突即使在组成员之间也存在。
	 */
	if (LOCK_LOCKTAG(*fc_lock) == LOCKTAG_RELATION_EXTEND)
	{
		fc_PROCLOCK_PRINT("LockCheckConflicts: conflicting (group)",
					   fc_proclock);
		return true;
	}

	/*
	 * 我们锁组成员以冲突模式持有的锁不是实际的冲突；
	 * 我们可以将这些减去，并查看我们是否仍然存在冲突。这在持有或等待此对象锁的进程数量上是O(N)。
	 * 我们可以通过使共享内存状态更加复杂（和更大）来改进此点，但似乎不值得。
	 */
	fc_procLocks = &(fc_lock->procLocks);
	fc_otherproclock = (PROCLOCK *)
		SHMQueueNext(fc_procLocks, fc_procLocks, offsetof(PROCLOCK, lockLink));
	while (fc_otherproclock != NULL)
	{
		if (fc_proclock != fc_otherproclock &&
			fc_proclock->groupLeader == fc_otherproclock->groupLeader &&
			(fc_otherproclock->holdMask & fc_conflictMask) != 0)
		{
			int			fc_intersectMask = fc_otherproclock->holdMask & fc_conflictMask;

			for (fc_i = 1; fc_i <= fc_numLockModes; fc_i++)
			{
				if ((fc_intersectMask & LOCKBIT_ON(fc_i)) != 0)
				{
					if (fc_conflictsRemaining[fc_i] <= 0)
						elog(PANIC, "proclocks held do not match lock");
					fc_conflictsRemaining[fc_i]--;
					fc_totalConflictsRemaining--;
				}
			}

			if (fc_totalConflictsRemaining == 0)
			{
				fc_PROCLOCK_PRINT("LockCheckConflicts: resolved (group)",
							   fc_proclock);
				return false;
			}
		}
		fc_otherproclock = (PROCLOCK *)
			SHMQueueNext(fc_procLocks, &fc_otherproclock->lockLink,
						 offsetof(PROCLOCK, lockLink));
	}

	/* 不，这是真正的冲突。 */
	fc_PROCLOCK_PRINT("LockCheckConflicts: conflicting (group)", fc_proclock);
	return true;
}

/*
 * GrantLock -- 更新锁和proclock数据结构，以显示
 *		锁请求已被授予。
 *
 * 注：如果进程被阻塞，它还需要从等待列表中移除
 * 并清除其waitLock/waitProcLock字段。这在这里没有执行。
 *
 * 注：锁授予还必须记录在相关的LOCALLOCK表条目中；
 * 但由于我们可能在唤醒其他进程，因此不能在此执行；
 * 而是由GrantLockLocal执行。
 */
void GrantLock(LOCK *fc_lock, PROCLOCK *fc_proclock, LOCKMODE fc_lockmode)
{
	fc_lock->nGranted++;
	fc_lock->granted[fc_lockmode]++;
	fc_lock->grantMask |= LOCKBIT_ON(fc_lockmode);
	if (fc_lock->granted[fc_lockmode] == fc_lock->requested[fc_lockmode])
		fc_lock->waitMask &= LOCKBIT_OFF(fc_lockmode);
	fc_proclock->holdMask |= LOCKBIT_ON(fc_lockmode);
	fc_LOCK_PRINT("GrantLock", fc_lock, fc_lockmode);
	Assert((fc_lock->nGranted > 0) && (fc_lock->granted[fc_lockmode] > 0));
	Assert(fc_lock->nGranted <= fc_lock->nRequested);
}

/*
 * UnGrantLock -- GrantLock的对立面。
 *
 * 更新锁和proclock数据结构，以显示当前持有者
 * 不再持有或请求该锁。
 *
 * 如果有任何等待在该锁上的等待者，现在应该被ProcLockWakeup唤醒，则返回true。
 */
static bool fc_UnGrantLock(LOCK *fc_lock, LOCKMODE fc_lockmode,
			PROCLOCK *fc_proclock, LockMethod fc_lockMethodTable)
{
	bool		fc_wakeupNeeded = false;

	Assert((fc_lock->nRequested > 0) && (fc_lock->requested[fc_lockmode] > 0));
	Assert((fc_lock->nGranted > 0) && (fc_lock->granted[fc_lockmode] > 0));
	Assert(fc_lock->nGranted <= fc_lock->nRequested);

	/*
	 * 修复一般锁统计信息
	 */
	fc_lock->nRequested--;
	fc_lock->requested[fc_lockmode]--;
	fc_lock->nGranted--;
	fc_lock->granted[fc_lockmode]--;

	if (fc_lock->granted[fc_lockmode] == 0)
	{
		/* 更改冲突掩码。 不再有这种锁类型。 */
		fc_lock->grantMask &= LOCKBIT_OFF(fc_lockmode);
	}

	fc_LOCK_PRINT("UnGrantLock: updated", fc_lock, fc_lockmode);

	/*
	 * 只有在释放的锁至少与等待者请求的锁类型中的一种冲突时，我们才需要运行ProcLockWakeup。
	 * 否则，任何导致他们等待的冲突必须仍然存在。
	 * 注：在MVCC之前，如果lock->granted[lockmode]仍然为正，我们可以跳过唤醒。
	 * 但现在不再如此，因为剩余授予的锁可能属于某些等待者，
	 * 他们现在可以被唤醒，因为他们与自己的锁没有冲突。
	 */
	if (fc_lockMethodTable->conflictTab[fc_lockmode] & fc_lock->waitMask)
		fc_wakeupNeeded = true;

	/*
	 * 现在修复每个proclock状态。
	 */
	fc_proclock->holdMask &= LOCKBIT_OFF(fc_lockmode);
	fc_PROCLOCK_PRINT("UnGrantLock: updated", fc_proclock);

	return fc_wakeupNeeded;
}

/*
 * CleanUpLock -- 在释放锁后进行清理。
 * 如果可能，我们垃圾收集proclock和锁对象，并在有剩余请求且调用者表示可以时调用ProcLockWakeup。
 * （通常，这应在UnGrantLock后调用，wakeupNeeded是来自UnGrantLock的结果。）
 *
 * 适当的分区锁必须在进入时持有，并将在退出时持有。
 */
static void fc_CleanUpLock(LOCK *fc_lock, PROCLOCK *fc_proclock,
			LockMethod fc_lockMethodTable, uint32 fc_hashcode,
			bool fc_wakeupNeeded)
{
	/*
	 * 如果这是我对该锁的最后一次持有，则删除我在proclock表中的条目。
	 */
	if (fc_proclock->holdMask == 0)
	{
		uint32		fc_proclock_hashcode;

		fc_PROCLOCK_PRINT("CleanUpLock: deleting", fc_proclock);
		SHMQueueDelete(&fc_proclock->lockLink);
		SHMQueueDelete(&fc_proclock->procLink);
		fc_proclock_hashcode = fc_ProcLockHashCode(&fc_proclock->tag, fc_hashcode);
		if (!hash_search_with_hash_value(LockMethodProcLockHash,
										 (void *) &(fc_proclock->tag),
										 fc_proclock_hashcode,
										 HASH_REMOVE,
										 NULL))
			elog(PANIC, "proclock table corrupted");
	}

	if (fc_lock->nRequested == 0)
	{
		/*
		 * 调用者刚刚释放了最后一个锁，因此垃圾收集锁对象。
		 */
		fc_LOCK_PRINT("CleanUpLock: deleting", fc_lock, 0);
		Assert(SHMQueueEmpty(&(fc_lock->procLocks)));
		if (!hash_search_with_hash_value(LockMethodLockHash,
										 (void *) &(fc_lock->tag),
										 fc_hashcode,
										 HASH_REMOVE,
										 NULL))
			elog(PANIC, "lock table corrupted");
	}
	else if (fc_wakeupNeeded)
	{
		/* 该锁上有等待者，因此唤醒他们。 */
		ProcLockWakeup(fc_lockMethodTable, fc_lock);
	}
}

/*
 * GrantLockLocal -- 更新locallock数据结构，以显示
 *		锁请求已被授予。
 *
 * 我们期望LockAcquire确保有空间添加一个新的
 * ResourceOwner条目。
 */
static void fc_GrantLockLocal(LOCALLOCK *fc_locallock, ResourceOwner fc_owner)
{
	LOCALLOCKOWNER *fc_lockOwners = fc_locallock->lockOwners;
	int			fc_i;

	Assert(fc_locallock->numLockOwners < fc_locallock->maxLockOwners);
	/* 计算总数 */
	fc_locallock->nLocks++;
	/* 计算每个所有者的锁 */
	for (fc_i = 0; fc_i < fc_locallock->numLockOwners; fc_i++)
	{
		if (fc_lockOwners[fc_i].owner == fc_owner)
		{
			fc_lockOwners[fc_i].nLocks++;
			return;
		}
	}
	fc_lockOwners[fc_i].owner = fc_owner;
	fc_lockOwners[fc_i].nLocks = 1;
	fc_locallock->numLockOwners++;
	if (fc_owner != NULL)
		ResourceOwnerRememberLock(fc_owner, fc_locallock);

	/* 表示锁已被某些类型的锁获取。 */
	fc_CheckAndSetLockHeld(fc_locallock, true);
}

/*
 * BeginStrongLockAcquire - 禁用对给定 LOCALLOCK 的快速路径使用， 
 * 并安排在失败时进行错误清理
 */
static void fc_BeginStrongLockAcquire(LOCALLOCK *fc_locallock, uint32 fc_fasthashcode)
{
	Assert(StrongLockInProgress == NULL);
	Assert(fc_locallock->holdsStrongLockCount == false);

	/*
	 * 向内存位置添加内容不是原子的，因此我们采用自旋锁来确保 
	 * 不与其他尝试同时增加计数的人发生冲突。
	 *
	 * XXX：在支持的架构上，这里可能值得考虑使用原子获取和添加指令。
	 */

	SpinLockAcquire(&FastPathStrongRelationLocks->mutex);
	FastPathStrongRelationLocks->count[fc_fasthashcode]++;
	fc_locallock->holdsStrongLockCount = true;
	StrongLockInProgress = fc_locallock;
	SpinLockRelease(&FastPathStrongRelationLocks->mutex);
}

/*
 * FinishStrongLockAcquire - 一旦不再需要，取消强锁获取的待处理清理。
 */
static void fc_FinishStrongLockAcquire(void)
{
	StrongLockInProgress = NULL;
}

/*
 * AbortStrongLockAcquire - 撤销由 BeginStrongLockAcquire 执行的强锁状态更改。
 */
void AbortStrongLockAcquire(void)
{
	uint32		fc_fasthashcode;
	LOCALLOCK  *fc_locallock = StrongLockInProgress;

	if (fc_locallock == NULL)
		return;

	fc_fasthashcode = FastPathStrongLockHashPartition(fc_locallock->hashcode);
	Assert(fc_locallock->holdsStrongLockCount == true);
	SpinLockAcquire(&FastPathStrongRelationLocks->mutex);
	Assert(FastPathStrongRelationLocks->count[fc_fasthashcode] > 0);
	FastPathStrongRelationLocks->count[fc_fasthashcode]--;
	fc_locallock->holdsStrongLockCount = false;
	StrongLockInProgress = NULL;
	SpinLockRelease(&FastPathStrongRelationLocks->mutex);
}

/*
 * GrantAwaitedLock -- 调用 GrantLockLocal 以获取我们正在进行 WaitOnLock 的锁。
 *
 * proc.c 需要在超时驱逐我们出锁的情况下使用此函数，但发现有人还是授予了我们锁。
 *
 * 我们可以直接导出 GrantLockLocal，但这会要求在 lock.h 中包含 resowner.h，这会产生循环依赖。
 */
void GrantAwaitedLock(void)
{
	fc_GrantLockLocal(awaitedLock, awaitedOwner);
}

/*
 * MarkLockClear -- 将已获取的锁标记为“清除”
 *
 * 这意味着我们知道在获取此锁之前，我们已经吸收了其他会话生成的所有 sinval 消息，因此我们可以自信地假设我们知道此锁保护的任何目录更改。
 */
void MarkLockClear(LOCALLOCK *fc_locallock)
{
	Assert(fc_locallock->nLocks > 0);
	fc_locallock->lockCleared = true;
}

/*
 * WaitOnLock -- 等待获取锁
 *
 * 调用者必须设置 MyProc->heldLocks，以反映此进程在可锁对象上已经持有的锁。
 *
 * 在进入时必须持有适当的分区锁。
 */
static void fc_WaitOnLock(LOCALLOCK *fc_locallock, ResourceOwner fc_owner)
{
	LOCKMETHODID fc_lockmethodid = LOCALLOCK_LOCKMETHOD(*fc_locallock);
	LockMethod	fc_lockMethodTable = LockMethods[fc_lockmethodid];
	char	   *volatile fc_new_status = NULL;

	fc_LOCK_PRINT("WaitOnLock: sleeping on lock",
			   fc_locallock->lock, fc_locallock->tag.mode);

	/* 报告状态更改为等待状态 */
	if (update_process_title)
	{
		const char *fc_old_status;
		int			fc_len;

		fc_old_status = get_ps_display(&fc_len);
		fc_new_status = (char *) palloc(fc_len + 8 + 1);
		memcpy(fc_new_status, fc_old_status, fc_len);
		strcpy(fc_new_status + fc_len, " waiting");
		set_ps_display(fc_new_status);
		fc_new_status[fc_len] = '\0'; /* 截断掉 " waiting" */
	}

	awaitedLock = fc_locallock;
	awaitedOwner = fc_owner;

	/*
	 * 注意：请不要在正常或失败路径中将任何共享状态清理放在对 ProcSleep 的调用之后。锁状态必须由锁授予者完全设置，或者在我们放弃等待锁时由 CheckDeadLock 设置。这是必要的，因为可能会有取消/死亡中断干扰 ProcSleep，在其他人授予我们锁之前，但在我们注意到之前。因此，在授予之后，锁表状态必须完全反映我们拥有锁的事实；我们不能在返回时进行其他工作。
	 *
	 * 我们可以使用 PG_TRY 块尝试在失败后进行清理，但这仍然有一个主要限制：在等待时可能会发生 elog(FATAL)（例如，“死亡”中断），然后控制不会返回到这里。因此，所有重要状态的清理应该在 LockErrorCleanup 中进行，而不是在这里。我们可以使用 PG_TRY 清除“等待”状态标志，因为如果进程退出，执行该操作是不重要的。
	 */
	PG_TRY();
	{
		if (ProcSleep(fc_locallock, fc_lockMethodTable) != PROC_WAIT_STATUS_OK)
		{
			/*
			 * 我们由于死锁而失败，见 CheckDeadLock()。现在退出。
			 */
			awaitedLock = NULL;
			fc_LOCK_PRINT("WaitOnLock: aborting on lock",
					   fc_locallock->lock, fc_locallock->tag.mode);
			LWLockRelease(LockHashPartitionLock(fc_locallock->hashcode));

			/*
			 * 现在我们不再持有分区锁，可以提供包含检测到的死锁详细信息的错误报告。
			 */
			DeadLockReport();
			/* 未到达 */
		}
	}
	PG_CATCH();
	{
		/* 在此路径中，awaitedLock 仍然设置，直到 LockErrorCleanup */

		/* 报告状态更改为非等待状态 */
		if (update_process_title)
		{
			set_ps_display(fc_new_status);
			pfree(fc_new_status);
		}

		/* 并传播错误 */
		PG_RE_THROW();
	}
	PG_END_TRY();

	awaitedLock = NULL;

	/* 报告状态更改为非等待状态 */
	if (update_process_title)
	{
		set_ps_display(fc_new_status);
		pfree(fc_new_status);
	}

	fc_LOCK_PRINT("WaitOnLock: wakeup on lock",
			   fc_locallock->lock, fc_locallock->tag.mode);
}

/*
 * 从其所在的等待队列中移除一个进程（调用者必须知道它在一个队列中）。
 * 这仅在进程未能获取锁时使用，因此我们将其 waitStatus 设置为 PROC_WAIT_STATUS_ERROR。
 *
 * 调用者必须持有适当的分区锁。此外，如果需要，调用者负责通知该进程。
 *
 * 注意：这不会清理任何可能存在的用于锁的 locallock 对象。
 */
void RemoveFromWaitQueue(PGPROC *fc_proc, uint32 fc_hashcode)
{
	LOCK	   *fc_waitLock = fc_proc->waitLock;
	PROCLOCK   *fc_proclock = fc_proc->waitProcLock;
	LOCKMODE	fc_lockmode = fc_proc->waitLockMode;
	LOCKMETHODID fc_lockmethodid = LOCK_LOCKMETHOD(*fc_waitLock);

	/* 确保进程正在等待 */
	Assert(fc_proc->waitStatus == PROC_WAIT_STATUS_WAITING);
	Assert(fc_proc->links.next != NULL);
	Assert(fc_waitLock);
	Assert(fc_waitLock->waitProcs.size > 0);
	Assert(0 < fc_lockmethodid && fc_lockmethodid < lengthof(LockMethods));

	/* 从锁的等待队列中移除进程 */
	SHMQueueDelete(&(fc_proc->links));
	fc_waitLock->waitProcs.size--;

	/* 撤销等待进程的请求计数的递增 */
	Assert(fc_waitLock->nRequested > 0);
	Assert(fc_waitLock->nRequested > fc_proc->waitLock->nGranted);
	fc_waitLock->nRequested--;
	Assert(fc_waitLock->requested[fc_lockmode] > 0);
	fc_waitLock->requested[fc_lockmode]--;
	/* 如果适用，不要忘记清除 waitMask 位 */
	if (fc_waitLock->granted[fc_lockmode] == fc_waitLock->requested[fc_lockmode])
		fc_waitLock->waitMask &= LOCKBIT_OFF(fc_lockmode);

	/* 清理进程自己的状态，并传递 ok/fail 信号 */
	fc_proc->waitLock = NULL;
	fc_proc->waitProcLock = NULL;
	fc_proc->waitStatus = PROC_WAIT_STATUS_ERROR;

	/*
	 * 如果 proclock 不代表任何已持有的锁，则立即删除它。
	 * （这必须现在发生，因为如果锁的所有者决定释放它，并且请求/授予计数随后归零，
	 * LockRelease 期望没有剩余的 proclocks。）然后查看是否可以唤醒锁的其他等待者。
	 */
	fc_CleanUpLock(fc_waitLock, fc_proclock,
				LockMethods[fc_lockmethodid], fc_hashcode,
				true);
}

/*
 * LockRelease -- 查找 'locktag' 并释放一个 'lockmode' 锁。
 * 如果 'sessionLock' 为真，则释放会话锁，否则释放常规事务锁。
 *
 * 副作用：查找现在可以唤醒的任何等待进程，授予他们请求的锁并唤醒他们。
 * （我们必须在这里授予锁，以避免唤醒的进程与其他新进程之间的竞争，
 * 后者可能会请求锁。）
 */
bool LockRelease(const LOCKTAG *fc_locktag, LOCKMODE fc_lockmode, bool fc_sessionLock)
{
	LOCKMETHODID fc_lockmethodid = fc_locktag->locktag_lockmethodid;
	LockMethod	fc_lockMethodTable;
	LOCALLOCKTAG fc_localtag;
	LOCALLOCK  *fc_locallock;
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	LWLock	   *fc_partitionLock;
	bool		fc_wakeupNeeded;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);
	fc_lockMethodTable = LockMethods[fc_lockmethodid];
	if (fc_lockmode <= 0 || fc_lockmode > fc_lockMethodTable->numLockModes)
		elog(ERROR, "unrecognized lock mode: %d", fc_lockmode);

#ifdef LOCK_DEBUG
	if (fc_LOCK_DEBUG_ENABLED(fc_locktag))
		elog(LOG, "LockRelease: lock [%u,%u] %s",
			 fc_locktag->locktag_field1, fc_locktag->locktag_field2,
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
#endif

	/*
	 * 查找此锁和锁模式的LOCALLOCK条目
	 */
	MemSet(&fc_localtag, 0, sizeof(fc_localtag)); /* 必须清除填充 */
	fc_localtag.lock = *fc_locktag;
	fc_localtag.mode = fc_lockmode;

	fc_locallock = (LOCALLOCK *) hash_search(LockMethodLocalHash,
										  (void *) &fc_localtag,
										  HASH_FIND, NULL);

	/*
	 * 让调用者自己打印错误消息，也可以。不要ereport(ERROR)。
	 */
	if (!fc_locallock || fc_locallock->nLocks <= 0)
	{
		elog(WARNING, "you don't own a lock of type %s",
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
		return false;
	}

	/*
	 * 减少资源所有者的计数。
	 */
	{
		LOCALLOCKOWNER *fc_lockOwners = fc_locallock->lockOwners;
		ResourceOwner fc_owner;
		int			fc_i;

		/* 识别锁的所有者 */
		if (fc_sessionLock)
			fc_owner = NULL;
		else
			fc_owner = CurrentResourceOwner;

		for (fc_i = fc_locallock->numLockOwners - 1; fc_i >= 0; fc_i--)
		{
			if (fc_lockOwners[fc_i].owner == fc_owner)
			{
				Assert(fc_lockOwners[fc_i].nLocks > 0);
				if (--fc_lockOwners[fc_i].nLocks == 0)
				{
					if (fc_owner != NULL)
						ResourceOwnerForgetLock(fc_owner, fc_locallock);
					/* 压缩未使用的槽位 */
					fc_locallock->numLockOwners--;
					if (fc_i < fc_locallock->numLockOwners)
						fc_lockOwners[fc_i] = fc_lockOwners[fc_locallock->numLockOwners];
				}
				break;
			}
		}
		if (fc_i < 0)
		{
			/* 不要释放属于其他所有者的锁 */
			elog(WARNING, "you don't own a lock of type %s",
				 fc_lockMethodTable->lockModeNames[fc_lockmode]);
			return false;
		}
	}

	/*
	 * 减少总的本地计数。如果我们仍然持有锁，我们就完成了。
	 */
	fc_locallock->nLocks--;

	if (fc_locallock->nLocks > 0)
		return true;

	/*
	 * 此时我们无法再假设我们清除了与此锁相关的无效消息。
	 * 尽管我们将在从此例程的任何故意返回之前删除 LOCALLOCK 对象，但现在显式重置 lockCleared 似乎值得，因为万一某个错误阻止我们删除 LOCALLOCK。
	 */
	fc_locallock->lockCleared = false;

	/* 尝试快速释放任何符合快速路径的锁。 */
	if (EligibleForRelationFastPath(fc_locktag, fc_lockmode) &&
		FastPathLocalUseCount > 0)
	{
		bool		fc_released;

		/*
		 * 我们可能不会在这里找到锁，即使我们最初在这里进入。
		 * 另一个后端可能已将其移动到主表。
		 */
		LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);
		fc_released = fc_FastPathUnGrantRelationLock(fc_locktag->locktag_field2,
											   fc_lockmode);
		LWLockRelease(&MyProc->fpInfoLock);
		if (fc_released)
		{
			fc_RemoveLocalLock(fc_locallock);
			return true;
		}
	}

	/*
	 * 否则，我们不得不处理共享锁表。
	 */
	fc_partitionLock = LockHashPartitionLock(fc_locallock->hashcode);

	LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

	
/***********************************************
	 * 通常，我们不需要重新找到锁或 proclock，因为我们在
	 * locallock 表中保持了它们的地址，并且在我们持有
	 * 锁时，它们不可能被移除。但是，锁可能通过快速路径被获取，
	 * 其后被另一个后端移动到主哈希表，在这种情况下我们需要在此查找对象。
	 * 我们假设如果是这样，锁字段为 NULL。
	 */
	fc_lock = fc_locallock->lock;
	if (!fc_lock)
	{
		PROCLOCKTAG fc_proclocktag;

		Assert(EligibleForRelationFastPath(fc_locktag, fc_lockmode));
		fc_lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
													(const void *) fc_locktag,
													fc_locallock->hashcode,
													HASH_FIND,
													NULL);
		if (!fc_lock)
			elog(ERROR, "failed to re-find shared lock object");
		fc_locallock->lock = fc_lock;

		fc_proclocktag.myLock = fc_lock;
		fc_proclocktag.myProc = MyProc;
		fc_locallock->proclock = (PROCLOCK *) hash_search(LockMethodProcLockHash,
													   (void *) &fc_proclocktag,
													   HASH_FIND,
													   NULL);
		if (!fc_locallock->proclock)
			elog(ERROR, "failed to re-find shared proclock object");
	}
	fc_LOCK_PRINT("LockRelease: found", fc_lock, fc_lockmode);
	fc_proclock = fc_locallock->proclock;
	fc_PROCLOCK_PRINT("LockRelease: found", fc_proclock);

	/*
	 * 再次检查我们实际上是否持有我们想要释放的锁的类型。
	 */
	if (!(fc_proclock->holdMask & LOCKBIT_ON(fc_lockmode)))
	{
		fc_PROCLOCK_PRINT("LockRelease: WRONGTYPE", fc_proclock);
		LWLockRelease(fc_partitionLock);
		elog(WARNING, "you don't own a lock of type %s",
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
		fc_RemoveLocalLock(fc_locallock);
		return false;
	}

	/***********************************************
	 * 进行释放。CleanUpLock 将唤醒任何现在可以醒来的等待者。
	 */
	fc_wakeupNeeded = fc_UnGrantLock(fc_lock, fc_lockmode, fc_proclock, fc_lockMethodTable);

	fc_CleanUpLock(fc_lock, fc_proclock,
				fc_lockMethodTable, fc_locallock->hashcode,
				fc_wakeupNeeded);

	LWLockRelease(fc_partitionLock);

	fc_RemoveLocalLock(fc_locallock);
	return true;
}

/***********************************************
 * LockReleaseAll -- 释放当前进程持有的指定锁方法的所有锁。
 *
 * 嗯，不一定是 *所有* 锁。可用的行为是：
 *		allLocks == true：释放所有锁，包括会话锁。
 *		allLocks == false：释放所有非会话锁。
 */
void LockReleaseAll(LOCKMETHODID fc_lockmethodid, bool fc_allLocks)
{
	HASH_SEQ_STATUS fc_status;
	LockMethod	fc_lockMethodTable;
	int			fc_i,
				fc_numLockModes;
	LOCALLOCK  *fc_locallock;
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	int			fc_partition;
	bool		fc_have_fast_path_lwlock = false;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);
	fc_lockMethodTable = LockMethods[fc_lockmethodid];

#ifdef LOCK_DEBUG
	if (*(fc_lockMethodTable->trace_flag))
		elog(LOG, "LockReleaseAll: lockmethod=%d", fc_lockmethodid);
#endif

	/***********************************************
	 * 如果合适，处理我们的快速路径 VXID 锁。注意，这是
	 * 我们持有的关于自己 VXID 的锁唯一可被释放的方式：
	 * 只有在顶级事务结束时才会释放。
	 */
	if (fc_lockmethodid == DEFAULT_LOCKMETHOD)
		VirtualXactLockTableCleanup();

	fc_numLockModes = fc_lockMethodTable->numLockModes;

	/***********************************************
	 * 首先，我们遍历 locallock 表，清除不需要的条目，
	 * 然后扫描进程的 proclocks 并清除这些条目。我们
	 * 单独执行此操作，因为我们可能有多个 locallock 条目
	 * 指向同一个 proclock，我们不敢最后留下任何悬挂
	 * 指针。不过，快速路径锁是在 locallock 表
	 * 扫描期间清理的。
	 */
	hash_seq_init(&fc_status, LockMethodLocalHash);

	while ((fc_locallock = (LOCALLOCK *) hash_seq_search(&fc_status)) != NULL)
	{
		/*
		 * 如果LOCALLOCK条目未被使用，我们必须是在尝试设置此锁时耗尽了共享内存。 
		 * 只需忘记该本地条目。
		 */
		if (fc_locallock->nLocks == 0)
		{
			fc_RemoveLocalLock(fc_locallock);
			continue;
		}

		/* 忽略不是要删除的lockmethod的项目 */
		if (LOCALLOCK_LOCKMETHOD(*fc_locallock) != fc_lockmethodid)
			continue;

		/*
		 * 如果我们被要求释放所有锁，我们只需删除该条目。
		 * 否则，必须扫描以查看是否有会话锁。我们假设会话锁最多只有一个lockOwners条目。
		 */
		if (!fc_allLocks)
		{
			LOCALLOCKOWNER *fc_lockOwners = fc_locallock->lockOwners;

			/* 如果会话锁在数组位置0之上，将其移至0 */
			for (fc_i = 0; fc_i < fc_locallock->numLockOwners; fc_i++)
			{
				if (fc_lockOwners[fc_i].owner == NULL)
					fc_lockOwners[0] = fc_lockOwners[fc_i];
				else
					ResourceOwnerForgetLock(fc_lockOwners[fc_i].owner, fc_locallock);
			}

			if (fc_locallock->numLockOwners > 0 &&
				fc_lockOwners[0].owner == NULL &&
				fc_lockOwners[0].nLocks > 0)
			{
				/* 修复locallock以仅显示会话锁 */
				fc_locallock->nLocks = fc_lockOwners[0].nLocks;
				fc_locallock->numLockOwners = 1;
				/* 我们不删除此locallock，因此完成 */
				continue;
			}
			else
				fc_locallock->numLockOwners = 0;
		}

#ifdef USE_ASSERT_CHECKING

		/*
		 * 元组锁当前仅在事务中短时间内持有。检查我们是否忘记释放一个。
		 */
		if (LOCALLOCK_LOCKTAG(*fc_locallock) == LOCKTAG_TUPLE && !fc_allLocks)
			elog(WARNING, "tuple lock held at commit");
#endif

		/*
		 * 如果锁或proclock指针为NULL，则此锁是通过关系快路径获取的（并且不知道是否已被转移）。
		 */
		if (fc_locallock->proclock == NULL || fc_locallock->lock == NULL)
		{
			LOCKMODE	fc_lockmode = fc_locallock->tag.mode;
			Oid			fc_relid;

			/* 验证我们得到的是快路径锁。 */
			if (!EligibleForRelationFastPath(&fc_locallock->tag.lock, fc_lockmode))
				elog(PANIC, "locallock table corrupted");

			/*
			 * 如果我们当前不持有保护我们快路径数据结构的LWLock，
			 * 我们必须在尝试通过快路径释放锁之前获取它。我们将继续
			 * 持有LWLock，直到我们完成扫描locallock表，
			 * 除非我们遇到了转移的快路径锁。(XXX 这真的是一个好主意吗？可能会有很多条目……)
			 */
			if (!fc_have_fast_path_lwlock)
			{
				LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);
				fc_have_fast_path_lwlock = true;
			}

			/* 尝试快路径释放。 */
			fc_relid = fc_locallock->tag.lock.locktag_field2;
			if (fc_FastPathUnGrantRelationLock(fc_relid, fc_lockmode))
			{
				fc_RemoveLocalLock(fc_locallock);
				continue;
			}

			/*
			 * 我们的锁，最初通过快路径获取，已被转移到主锁表。 
			 * 这将需要一些额外的工作，因此在开始之前释放我们的快路径锁。
			 */
			LWLockRelease(&MyProc->fpInfoLock);
			fc_have_fast_path_lwlock = false;

			/*
			 * 现在转储锁。我们在这种情况下没有指向LOCK或PROCLOCK的指针，
			 * 所以我们必须以不同于正常锁释放的方式处理这件事。 
			 * 不幸的是，这在partitionLock上需要额外的LWLock获取和释放周期，
			 * 但希望这不会经常发生。
			 */
			fc_LockRefindAndRelease(fc_lockMethodTable, MyProc,
								 &fc_locallock->tag.lock, fc_lockmode, false);
			fc_RemoveLocalLock(fc_locallock);
			continue;
		}

		/* 标记proclock以显示我们需要释放此锁模式 */
		if (fc_locallock->nLocks > 0)
			fc_locallock->proclock->releaseMask |= LOCKBIT_ON(fc_locallock->tag.mode);

		/* 并移除locallock哈希表条目 */
		fc_RemoveLocalLock(fc_locallock);
	}

	/* 完成快路径数据结构 */
	if (fc_have_fast_path_lwlock)
		LWLockRelease(&MyProc->fpInfoLock);

	/*
	 * 现在，分别扫描每个锁分区。
	 */
	for (fc_partition = 0; fc_partition < NUM_LOCK_PARTITIONS; fc_partition++)
	{
		LWLock	   *fc_partitionLock;
		SHM_QUEUE  *fc_procLocks = &(MyProc->myProcLocks[fc_partition]);
		PROCLOCK   *fc_nextplock;

		fc_partitionLock = LockHashPartitionLockByIndex(fc_partition);

		/*
		 * 如果该分区的proclock列表为空，我们可以跳过获取分区锁。
		 * 这个优化比看起来更棘手，因为另一个后端可能正在添加
		 * 某些内容到我们的proclock列表，因为提升了我们的
		 * 快路径锁中的一个。然而，任何这样的锁都必须是我们
		 * 决定不删除的，因此现在跳过它是可以的；
		 * 我们只会再次决定不删除它。我们必须小心在获取分区锁后重新获取
		 * 列表头，以确保我们有一个有效的、最新的指针。
		 * （如果指针获取/存储是原子的，可能没有重大风险，但我们不想假设这一点。）
		 *
		 * XXX 这个论点假设locallock表正确
		 * 表示我们所有的快路径锁。虽然allLocks模式
		 * 保证清理我们所有正常的锁而不考虑locallock情况，
		 * 但我们失去了对快路径锁的那个保证。
		 * 这并不理想。
		 */
		if (SHMQueueNext(fc_procLocks, fc_procLocks,
						 offsetof(PROCLOCK, procLink)) == NULL)
			continue;			/* 无需检查此分区 */

		LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

		for (fc_proclock = (PROCLOCK *) SHMQueueNext(fc_procLocks, fc_procLocks,
												  offsetof(PROCLOCK, procLink));
			 fc_proclock;
			 fc_proclock = fc_nextplock)
		{
			bool		fc_wakeupNeeded = false;

			/* 首先获取链接，因为我们可能会解除链接/删除这个proclock */
			fc_nextplock = (PROCLOCK *)
				SHMQueueNext(fc_procLocks, &fc_proclock->procLink,
							 offsetof(PROCLOCK, procLink));

			Assert(fc_proclock->tag.myProc == MyProc);

			fc_lock = fc_proclock->tag.myLock;

			/* 忽略不是要删除的lockmethod的项目 */
			if (LOCK_LOCKMETHOD(*fc_lock) != fc_lockmethodid)
				continue;

			/*
			 * 在allLocks模式下，强制释放所有锁，即使locallock
			 * 表存在问题
			 */
			if (fc_allLocks)
				fc_proclock->releaseMask = fc_proclock->holdMask;
			else
				Assert((fc_proclock->releaseMask & ~fc_proclock->holdMask) == 0);

			/*
			 * 忽略没有需要释放的项目，除非它们的holdMask == 0，因此可以回收
			 */
			if (fc_proclock->releaseMask == 0 && fc_proclock->holdMask != 0)
				continue;

			fc_PROCLOCK_PRINT("LockReleaseAll", fc_proclock);
			fc_LOCK_PRINT("LockReleaseAll", fc_lock, 0);
			Assert(fc_lock->nRequested >= 0);
			Assert(fc_lock->nGranted >= 0);
			Assert(fc_lock->nGranted <= fc_lock->nRequested);
			Assert((fc_proclock->holdMask & ~fc_lock->grantMask) == 0);

			/*
			 * 释放之前标记的锁模式
			 */
			for (fc_i = 1; fc_i <= fc_numLockModes; fc_i++)
			{
				if (fc_proclock->releaseMask & LOCKBIT_ON(fc_i))
					fc_wakeupNeeded |= fc_UnGrantLock(fc_lock, fc_i, fc_proclock,
												fc_lockMethodTable);
			}
			Assert((fc_lock->nRequested >= 0) && (fc_lock->nGranted >= 0));
			Assert(fc_lock->nGranted <= fc_lock->nRequested);
			fc_LOCK_PRINT("LockReleaseAll: updated", fc_lock, 0);

			fc_proclock->releaseMask = 0;

			/* CleanUpLock将在需要时唤醒等待者。 */
			fc_CleanUpLock(fc_lock, fc_proclock,
						fc_lockMethodTable,
						LockTagHashCode(&fc_lock->tag),
						fc_wakeupNeeded);
		}						/* 循环遍历该分区内的PROCLOCKs */

		LWLockRelease(fc_partitionLock);
	}							/* 循环遍历分区 */

#ifdef LOCK_DEBUG
	if (*(fc_lockMethodTable->trace_flag))
		elog(LOG, "LockReleaseAll done");
#endif
}

/*
 * LockReleaseSession -- 释放当前进程持有的指定锁方法的所有会话锁。
 */
void LockReleaseSession(LOCKMETHODID fc_lockmethodid)
{
	HASH_SEQ_STATUS fc_status;
	LOCALLOCK  *fc_locallock;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);

	hash_seq_init(&fc_status, LockMethodLocalHash);

	while ((fc_locallock = (LOCALLOCK *) hash_seq_search(&fc_status)) != NULL)
	{
		/* 忽略不是指定锁方法的项目 */
		if (LOCALLOCK_LOCKMETHOD(*fc_locallock) != fc_lockmethodid)
			continue;

		fc_ReleaseLockIfHeld(fc_locallock, true);
	}
}

/*
 * LockReleaseCurrentOwner
 *		释放属于CurrentResourceOwner的所有锁
 *
 * 如果调用方知道这些锁是什么，它可以将它们作为数组传递。
 * 当持有大量锁时，这会显著加快调用速度。
 * 否则，传递NULL作为locallocks，我们将遍历哈希
 * 表寻找它们。
 */
void LockReleaseCurrentOwner(LOCALLOCK **fc_locallocks, int fc_nlocks)
{
	if (fc_locallocks == NULL)
	{
		HASH_SEQ_STATUS fc_status;
		LOCALLOCK  *fc_locallock;

		hash_seq_init(&fc_status, LockMethodLocalHash);

		while ((fc_locallock = (LOCALLOCK *) hash_seq_search(&fc_status)) != NULL)
			fc_ReleaseLockIfHeld(fc_locallock, false);
	}
	else
	{
		int			fc_i;

		for (fc_i = fc_nlocks - 1; fc_i >= 0; fc_i--)
			fc_ReleaseLockIfHeld(fc_locallocks[fc_i], false);
	}
}


static void fc_ReleaseLockIfHeld(LOCALLOCK *fc_locallock, bool fc_sessionLock)
{
	ResourceOwner fc_owner;
	LOCALLOCKOWNER *fc_lockOwners;
	int			fc_i;

	/* 确定锁的所有者（必须与 LockRelease 匹配！） */
	if (fc_sessionLock)
		fc_owner = NULL;
	else
		fc_owner = CurrentResourceOwner;

	/* 扫描以查看是否有任何锁属于目标所有者 */
	fc_lockOwners = fc_locallock->lockOwners;
	for (fc_i = fc_locallock->numLockOwners - 1; fc_i >= 0; fc_i--)
	{
		if (fc_lockOwners[fc_i].owner == fc_owner)
		{
			Assert(fc_lockOwners[fc_i].nLocks > 0);
			if (fc_lockOwners[fc_i].nLocks < fc_locallock->nLocks)
			{
				/*
				 * 在忘记这个 ResourceOwner 后，我们仍然会持有此锁。
				 */
				fc_locallock->nLocks -= fc_lockOwners[fc_i].nLocks;
				/* 压缩未使用的槽位 */
				fc_locallock->numLockOwners--;
				if (fc_owner != NULL)
					ResourceOwnerForgetLock(fc_owner, fc_locallock);
				if (fc_i < fc_locallock->numLockOwners)
					fc_lockOwners[fc_i] = fc_lockOwners[fc_locallock->numLockOwners];
			}
			else
			{
				Assert(fc_lockOwners[fc_i].nLocks == fc_locallock->nLocks);
				/* 我们希望只调用一次 LockRelease */
				fc_lockOwners[fc_i].nLocks = 1;
				fc_locallock->nLocks = 1;
				if (!LockRelease(&fc_locallock->tag.lock,
								 fc_locallock->tag.mode,
								 fc_sessionLock))
					elog(WARNING, "ReleaseLockIfHeld: failed??");
			}
			break;
		}
	}
}

/*
 * LockReassignCurrentOwner
 *		将所有属于 CurrentResourceOwner 的锁重新分配给其父资源所有者。
 *
 * 如果调用者知道那些锁是什么，可以将它们作为数组传递。
 * 当持有很多锁时（例如 pg_dump 有一个大型架构），这将显著加速调用。
 * 否则，传递 NULL 作为 locallocks，我们会遍历我们的哈希表以找到它们。
 */
void LockReassignCurrentOwner(LOCALLOCK **fc_locallocks, int fc_nlocks)
{
	ResourceOwner fc_parent = ResourceOwnerGetParent(CurrentResourceOwner);

	Assert(fc_parent != NULL);

	if (fc_locallocks == NULL)
	{
		HASH_SEQ_STATUS fc_status;
		LOCALLOCK  *fc_locallock;

		hash_seq_init(&fc_status, LockMethodLocalHash);

		while ((fc_locallock = (LOCALLOCK *) hash_seq_search(&fc_status)) != NULL)
			fc_LockReassignOwner(fc_locallock, fc_parent);
	}
	else
	{
		int			fc_i;

		for (fc_i = fc_nlocks - 1; fc_i >= 0; fc_i--)
			fc_LockReassignOwner(fc_locallocks[fc_i], fc_parent);
	}
}

/*
 * LockReassignCurrentOwner 的子例程。将属于 CurrentResourceOwner 的给定锁重新分配给其父级。
 */
static void fc_LockReassignOwner(LOCALLOCK *fc_locallock, ResourceOwner fc_parent)
{
	LOCALLOCKOWNER *fc_lockOwners;
	int			fc_i;
	int			fc_ic = -1;
	int			fc_ip = -1;

	/*
	 * 扫描以查看是否有属于当前所有者或其父级的锁
	 */
	fc_lockOwners = fc_locallock->lockOwners;
	for (fc_i = fc_locallock->numLockOwners - 1; fc_i >= 0; fc_i--)
	{
		if (fc_lockOwners[fc_i].owner == CurrentResourceOwner)
			fc_ic = fc_i;
		else if (fc_lockOwners[fc_i].owner == fc_parent)
			fc_ip = fc_i;
	}

	if (fc_ic < 0)
		return;					/* 当前没有锁 */

	if (fc_ip < 0)
	{
		/* 父级没有槽，因此只需给它子级的槽 */
		fc_lockOwners[fc_ic].owner = fc_parent;
		ResourceOwnerRememberLock(fc_parent, fc_locallock);
	}
	else
	{
		/* 将子级的计数合并到父级中 */
		fc_lockOwners[fc_ip].nLocks += fc_lockOwners[fc_ic].nLocks;
		/* 压缩未使用的槽位 */
		fc_locallock->numLockOwners--;
		if (fc_ic < fc_locallock->numLockOwners)
			fc_lockOwners[fc_ic] = fc_lockOwners[fc_locallock->numLockOwners];
	}
	ResourceOwnerForgetLock(CurrentResourceOwner, fc_locallock);
}

/*
 * FastPathGrantRelationLock
 *		如果有空间，则使用每个后端快速路径数组授予锁。
 */
static bool fc_FastPathGrantRelationLock(Oid fc_relid, LOCKMODE fc_lockmode)
{
	uint32		fc_f;
	uint32		fc_unused_slot = FP_LOCK_SLOTS_PER_BACKEND;

	/* 扫描以查找此 relid 的现有条目，记住空槽。 */
	for (fc_f = 0; fc_f < FP_LOCK_SLOTS_PER_BACKEND; fc_f++)
	{
		if (FAST_PATH_GET_BITS(MyProc, fc_f) == 0)
			fc_unused_slot = fc_f;
		else if (MyProc->fpRelId[fc_f] == fc_relid)
		{
			Assert(!FAST_PATH_CHECK_LOCKMODE(MyProc, fc_f, fc_lockmode));
			FAST_PATH_SET_LOCKMODE(MyProc, fc_f, fc_lockmode);
			return true;
		}
	}

	/* 如果没有现有条目，则使用任何空槽。 */
	if (fc_unused_slot < FP_LOCK_SLOTS_PER_BACKEND)
	{
		MyProc->fpRelId[fc_unused_slot] = fc_relid;
		FAST_PATH_SET_LOCKMODE(MyProc, fc_unused_slot, fc_lockmode);
		++FastPathLocalUseCount;
		return true;
	}

	/* 没有现有条目，也没有空槽。 */
	return false;
}

/*
 * FastPathUnGrantRelationLock
 *		释放快速路径锁（如果存在）。更新后端专用的本地使用计数。
 */
static bool fc_FastPathUnGrantRelationLock(Oid fc_relid, LOCKMODE fc_lockmode)
{
	uint32		fc_f;
	bool		fc_result = false;

	FastPathLocalUseCount = 0;
	for (fc_f = 0; fc_f < FP_LOCK_SLOTS_PER_BACKEND; fc_f++)
	{
		if (MyProc->fpRelId[fc_f] == fc_relid
			&& FAST_PATH_CHECK_LOCKMODE(MyProc, fc_f, fc_lockmode))
		{
			Assert(!fc_result);
			FAST_PATH_CLEAR_LOCKMODE(MyProc, fc_f, fc_lockmode);
			fc_result = true;
			/* 我们继续迭代，以更新 FastPathLocalUseCount */
		}
		if (FAST_PATH_GET_BITS(MyProc, fc_f) != 0)
			++FastPathLocalUseCount;
	}
	return fc_result;
}

/*
 * FastPathTransferRelationLocks
 *		将与给定锁标签匹配的锁从每个后端快速路径数组转移到共享哈希表。
 *
 * 如果成功，则返回 true，如果用尽共享内存，则返回 false。
 */
static bool fc_FastPathTransferRelationLocks(LockMethod fc_lockMethodTable, const LOCKTAG *fc_locktag,
							  uint32 fc_hashcode)
{
	LWLock	   *fc_partitionLock = LockHashPartitionLock(fc_hashcode);
	Oid			fc_relid = fc_locktag->locktag_field2;
	uint32		fc_i;

	/*
	 * 每个可能持有快速路径锁的 PGPROC 都存在于 ProcGlobal->allProcs 中。
	 * 准备好的事务不在其中，但任何由准备好的事务持有的未决快速路径锁
	 * 都会转移到主锁表。
	 */
	for (fc_i = 0; fc_i < ProcGlobal->allProcCount; fc_i++)
	{
		PGPROC	   *fc_proc = &ProcGlobal->allProcs[fc_i];
		uint32		fc_f;

		LWLockAcquire(&fc_proc->fpInfoLock, LW_EXCLUSIVE);

		/*
		 * 如果目标后端没有引用与锁相同的数据库，则我们根本不需要检查单个关系 ID；
		 * 其中没有任何一个可能相关。
		 *
		 * proc->databaseId 在后端启动时设置，并且此后永远不会更改，
		 * 因此在获取 &proc->fpInfoLock 之前执行此测试可能是安全的。
		 * 特别是，如果目标后端持有任何快速路径锁，则可以安全地假设它必须在设置 proc->databaseId 之后
		 * 执行了内存屏障操作（特别是 LWLock 获取）。
		 * 不过，自从其他后端设置 proc->databaseId 后我们的后端是否一定进行了内存屏障操作就不那么明确了。
		 * 因此，为了安全起见，我们在获取 LWLock 之后再进行测试。
		 */
		if (fc_proc->databaseId != fc_locktag->locktag_field1)
		{
			LWLockRelease(&fc_proc->fpInfoLock);
			continue;
		}

		for (fc_f = 0; fc_f < FP_LOCK_SLOTS_PER_BACKEND; fc_f++)
		{
			uint32		fc_lockmode;

			/* 寻找匹配给定 relid 的已分配槽。 */
			if (fc_relid != fc_proc->fpRelId[fc_f] || FAST_PATH_GET_BITS(fc_proc, fc_f) == 0)
				continue;

			/* 查找或创建锁对象。 */
			LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);
			for (fc_lockmode = FAST_PATH_LOCKNUMBER_OFFSET;
				 fc_lockmode < FAST_PATH_LOCKNUMBER_OFFSET + FAST_PATH_BITS_PER_SLOT;
				 ++fc_lockmode)
			{
				PROCLOCK   *fc_proclock;

				if (!FAST_PATH_CHECK_LOCKMODE(fc_proc, fc_f, fc_lockmode))
					continue;
				fc_proclock = fc_SetupLockInTable(fc_lockMethodTable, fc_proc, fc_locktag,
											fc_hashcode, fc_lockmode);
				if (!fc_proclock)
				{
					LWLockRelease(fc_partitionLock);
					LWLockRelease(&fc_proc->fpInfoLock);
					return false;
				}
				GrantLock(fc_proclock->tag.myLock, fc_proclock, fc_lockmode);
				FAST_PATH_CLEAR_LOCKMODE(fc_proc, fc_f, fc_lockmode);
			}
			LWLockRelease(fc_partitionLock);

			/* 无需检查剩余槽。 */
			break;
		}
		LWLockRelease(&fc_proc->fpInfoLock);
	}
	return true;
}

/*
 * FastPathGetRelationLockEntry
 *		返回通过快速路径原本获得的锁的 PROCLOCK，
 *		如果必要，将其转移到主锁表中。
 *
 * 注意：调用者负责更新 locallock 对象。
 */
static PROCLOCK *
fc_FastPathGetRelationLockEntry(LOCALLOCK *fc_locallock)
{
	LockMethod	fc_lockMethodTable = LockMethods[DEFAULT_LOCKMETHOD];
	LOCKTAG    *fc_locktag = &fc_locallock->tag.lock;
	PROCLOCK   *fc_proclock = NULL;
	LWLock	   *fc_partitionLock = LockHashPartitionLock(fc_locallock->hashcode);
	Oid			fc_relid = fc_locktag->locktag_field2;
	uint32		fc_f;

	LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);

	for (fc_f = 0; fc_f < FP_LOCK_SLOTS_PER_BACKEND; fc_f++)
	{
		uint32		fc_lockmode;

		/* 寻找匹配给定 relid 的已分配槽。 */
		if (fc_relid != MyProc->fpRelId[fc_f] || FAST_PATH_GET_BITS(MyProc, fc_f) == 0)
			continue;

		/* 如果我们没有给定模式的锁，那就忘掉它！ */
		fc_lockmode = fc_locallock->tag.mode;
		if (!FAST_PATH_CHECK_LOCKMODE(MyProc, fc_f, fc_lockmode))
			break;

		/* 查找或创建锁对象。 */
		LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

		fc_proclock = fc_SetupLockInTable(fc_lockMethodTable, MyProc, fc_locktag,
									fc_locallock->hashcode, fc_lockmode);
		if (!fc_proclock)
		{
			LWLockRelease(fc_partitionLock);
			LWLockRelease(&MyProc->fpInfoLock);
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of shared memory"),
					 errhint("You might need to increase max_locks_per_transaction.")));
#ifdef FDD //cppcheck
			return NULL;
#endif
		}
		GrantLock(fc_proclock->tag.myLock, fc_proclock, fc_lockmode);
		FAST_PATH_CLEAR_LOCKMODE(MyProc, fc_f, fc_lockmode);

		LWLockRelease(fc_partitionLock);

		/* 无需检查剩余槽。 */
		break;
	}

	LWLockRelease(&MyProc->fpInfoLock);

	/* 锁可能已经被其他后端转移。 */
	if (fc_proclock == NULL)
	{
		LOCK	   *fc_lock;
		PROCLOCKTAG fc_proclocktag;
		uint32		fc_proclock_hashcode;

		LWLockAcquire(fc_partitionLock, LW_SHARED);

		fc_lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
													(void *) fc_locktag,
													fc_locallock->hashcode,
													HASH_FIND,
													NULL);
		if (!fc_lock)
			elog(ERROR, "failed to re-find shared lock object");

		fc_proclocktag.myLock = fc_lock;
		fc_proclocktag.myProc = MyProc;

		fc_proclock_hashcode = fc_ProcLockHashCode(&fc_proclocktag, fc_locallock->hashcode);
		fc_proclock = (PROCLOCK *)
			hash_search_with_hash_value(LockMethodProcLockHash,
										(void *) &fc_proclocktag,
										fc_proclock_hashcode,
										HASH_FIND,
										NULL);
		if (!fc_proclock)
			elog(ERROR, "failed to re-find shared proclock object");
		LWLockRelease(fc_partitionLock);
	}

	return fc_proclock;
}

/*
 * GetLockConflicts
 *		获取一个数组，包含当前持有与指定锁/锁模式冲突的锁的虚拟事务 ID。
 *		仅仅在等待该锁的事务不会被报告。
 *
 * 结果数组是 palloc 的，并且以无效的 VXID 结束。
 * 如果 *countp 不为 null，则更新为设置的项数。
 *
 * 当然，返回时结果可能已经过时，因此使用此功能时必须仔细考虑。
 * 类似地，具有“lxid”的 PGPROC 将被视为不冲突，无论它持有任何锁。
 * 现有调用者在该锁的 pg_xact 更新完成后不关心锁定者。
 * CommitTransaction() 在 pg_xact 更新后和释放锁之前清除“lxid”。
 *
 * 请注意，我们从不将当前 xact 的 vxid 包含在结果数组中，
 * 因为事务从不阻塞自己。
 */
VirtualTransactionId * GetLockConflicts(const LOCKTAG *fc_locktag, LOCKMODE fc_lockmode, int *fc_countp)
{
	static VirtualTransactionId *fc_vxids;
	LOCKMETHODID fc_lockmethodid = fc_locktag->locktag_lockmethodid;
	LockMethod	fc_lockMethodTable;
	LOCK	   *fc_lock;
	LOCKMASK	fc_conflictMask;
	SHM_QUEUE  *fc_procLocks;
	PROCLOCK   *fc_proclock;
	uint32		fc_hashcode;
	LWLock	   *fc_partitionLock;
	int			fc_count = 0;
	int			fc_fast_count = 0;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);
	fc_lockMethodTable = LockMethods[fc_lockmethodid];
	if (fc_lockmode <= 0 || fc_lockmode > fc_lockMethodTable->numLockModes)
		elog(ERROR, "unrecognized lock mode: %d", fc_lockmode);

	/*
	 * 分配内存以存储结果，并填充 InvalidVXID。我们只需要足够的空间来容纳 MaxBackends + max_prepared_xacts + 一个终止符。
	 * 在 HotStandby 中，在 TopMemoryContext 中分配一次。
	 */
	if (InHotStandby)
	{
		if (fc_vxids == NULL)
			fc_vxids = (VirtualTransactionId *)
				MemoryContextAlloc(TopMemoryContext,
								   sizeof(VirtualTransactionId) *
								   (MaxBackends + max_prepared_xacts + 1));
	}
	else
		fc_vxids = (VirtualTransactionId *)
			palloc0(sizeof(VirtualTransactionId) *
					(MaxBackends + max_prepared_xacts + 1));

	/* 计算哈希代码和分区锁，并查找冲突模式。 */
	fc_hashcode = LockTagHashCode(fc_locktag);
	fc_partitionLock = LockHashPartitionLock(fc_hashcode);
	fc_conflictMask = fc_lockMethodTable->conflictTab[fc_lockmode];

	/*
	 * 快速路径锁可能尚未输入主锁表。
	 * 如果我们正在处理的锁可能与此类锁冲突，则必须检查每个后端的快速路径数组以查找冲突。
	 */
	if (ConflictsWithRelationFastPath(fc_locktag, fc_lockmode))
	{
		int			fc_i;
		Oid			fc_relid = fc_locktag->locktag_field2;
		VirtualTransactionId fc_vxid;

		
		for (fc_i = 0; fc_i < ProcGlobal->allProcCount; fc_i++)
		{
			PGPROC	   *fc_proc = &ProcGlobal->allProcs[fc_i];
			uint32		fc_f;

			/* 后端从不阻塞自身 */
			if (fc_proc == MyProc)
				continue;

			LWLockAcquire(&fc_proc->fpInfoLock, LW_SHARED);

			/*
			 * 如果目标后端没有引用与锁相同的数据库，
			 * 我们就不需要检查各个关系ID；
			 * 因为它们都无关紧要。
			 *
			 * 有关为什么在获取锁后进行此测试的讨论，请参见 FastPathTransferRelationLocks()。
			 */
			if (fc_proc->databaseId != fc_locktag->locktag_field1)
			{
				LWLockRelease(&fc_proc->fpInfoLock);
				continue;
			}

			for (fc_f = 0; fc_f < FP_LOCK_SLOTS_PER_BACKEND; fc_f++)
			{
				uint32		fc_lockmask;

				/* 寻找匹配给定 relid 的已分配槽。 */
				if (fc_relid != fc_proc->fpRelId[fc_f])
					continue;
				fc_lockmask = FAST_PATH_GET_BITS(fc_proc, fc_f);
				if (!fc_lockmask)
					continue;
				fc_lockmask <<= FAST_PATH_LOCKNUMBER_OFFSET;

				/*
				 * 每个关系只能有一个条目，因此如果我们找到了它
				 * 且它没有冲突，我们可以跳过其余的槽位。
				 */
				if ((fc_lockmask & fc_conflictMask) == 0)
					break;

				/* 冲突！ */
				GET_VXID_FROM_PGPROC(fc_vxid, *fc_proc);

				if (VirtualTransactionIdIsValid(fc_vxid))
					fc_vxids[fc_count++] = fc_vxid;
				/* 否则，事务已经提交或中止 */

				/* 无需检查剩余槽。 */
				break;
			}

			LWLockRelease(&fc_proc->fpInfoLock);
		}
	}

	/* 记住我们发现的快速路径冲突数量。 */
	fc_fast_count = fc_count;

	/*
	 * 查找与标签匹配的锁对象。
	 */
	LWLockAcquire(fc_partitionLock, LW_SHARED);

	fc_lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
												(const void *) fc_locktag,
												fc_hashcode,
												HASH_FIND,
												NULL);
	if (!fc_lock)
	{
		/*
		 * 如果锁对象不存在，则没有任何锁住
		 * 该可锁定对象的内容。
		 */
		LWLockRelease(fc_partitionLock);
		fc_vxids[fc_count].backendId = InvalidBackendId;
		fc_vxids[fc_count].localTransactionId = InvalidLocalTransactionId;
		if (fc_countp)
			*fc_countp = fc_count;
		return fc_vxids;
	}

	/*
	 * 检查每个现有的持有者（或等待者）锁。
	 */

	fc_procLocks = &(fc_lock->procLocks);

	fc_proclock = (PROCLOCK *) SHMQueueNext(fc_procLocks, fc_procLocks,
										 offsetof(PROCLOCK, lockLink));

	while (fc_proclock)
	{
		if (fc_conflictMask & fc_proclock->holdMask)
		{
			PGPROC	   *fc_proc = fc_proclock->tag.myProc;

			/* 后端从不阻塞自身 */
			if (fc_proc != MyProc)
			{
				VirtualTransactionId fc_vxid;

				GET_VXID_FROM_PGPROC(fc_vxid, *fc_proc);

				if (VirtualTransactionIdIsValid(fc_vxid))
				{
					int			fc_i;

					/* 避免重复条目。 */
					for (fc_i = 0; fc_i < fc_fast_count; ++fc_i)
						if (VirtualTransactionIdEquals(fc_vxids[fc_i], fc_vxid))
							break;
					if (fc_i >= fc_fast_count)
						fc_vxids[fc_count++] = fc_vxid;
				}
				/* 否则，事务已经提交或中止 */
			}
		}

		fc_proclock = (PROCLOCK *) SHMQueueNext(fc_procLocks, &fc_proclock->lockLink,
											 offsetof(PROCLOCK, lockLink));
	}

	LWLockRelease(fc_partitionLock);

	if (fc_count > MaxBackends + max_prepared_xacts)	/* 永远不应该发生 */
		elog(PANIC, "too many conflicting locks found");

	fc_vxids[fc_count].backendId = InvalidBackendId;
	fc_vxids[fc_count].localTransactionId = InvalidLocalTransactionId;
	if (fc_countp)
		*fc_countp = fc_count;
	return fc_vxids;
}

/*
 * 在共享锁表中找到一个锁并释放它。调用者负责确认这样做是合理的。
 * （例如，如果仍可能存在指向它的 LOCALLOCK 对象，则在此释放锁显然是不好的。）
 *
 * 我们目前在两种情况下使用此方法：首先，释放在提交时
 * 被已准备事务持有的锁（请参见 lock_twophase_postcommit）；其次，
 * 释放通过快速路径获得的锁，转移到主哈希表中，然后释放（请参见 LockReleaseAll）。
 */
static void fc_LockRefindAndRelease(LockMethod fc_lockMethodTable, PGPROC *fc_proc,
					 LOCKTAG *fc_locktag, LOCKMODE fc_lockmode,
					 bool fc_decrement_strong_lock_count)
{
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	PROCLOCKTAG fc_proclocktag;
	uint32		fc_hashcode;
	uint32		fc_proclock_hashcode;
	LWLock	   *fc_partitionLock;
	bool		fc_wakeupNeeded;

	fc_hashcode = LockTagHashCode(fc_locktag);
	fc_partitionLock = LockHashPartitionLock(fc_hashcode);

	LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

	/*
	 * 重新查找锁对象（它最好在那里）。
	 */
	fc_lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
												(void *) fc_locktag,
												fc_hashcode,
												HASH_FIND,
												NULL);
	if (!fc_lock)
		elog(PANIC, "failed to re-find shared lock object");

	/*
	 * 重新查找 proclock 对象（同样）。
	 */
	fc_proclocktag.myLock = fc_lock;
	fc_proclocktag.myProc = fc_proc;

	fc_proclock_hashcode = fc_ProcLockHashCode(&fc_proclocktag, fc_hashcode);

	fc_proclock = (PROCLOCK *) hash_search_with_hash_value(LockMethodProcLockHash,
														(void *) &fc_proclocktag,
														fc_proclock_hashcode,
														HASH_FIND,
														NULL);
	if (!fc_proclock)
		elog(PANIC, "failed to re-find shared proclock object");

	/*
	 * 再次检查我们实际上是否持有我们想要释放的锁的类型。
	 */
	if (!(fc_proclock->holdMask & LOCKBIT_ON(fc_lockmode)))
	{
		fc_PROCLOCK_PRINT("lock_twophase_postcommit: WRONGTYPE", fc_proclock);
		LWLockRelease(fc_partitionLock);
		elog(WARNING, "you don't own a lock of type %s",
			 fc_lockMethodTable->lockModeNames[fc_lockmode]);
		return;
	}

	/***********************************************
	 * 进行释放。CleanUpLock 将唤醒任何现在可以醒来的等待者。
	 */
	fc_wakeupNeeded = fc_UnGrantLock(fc_lock, fc_lockmode, fc_proclock, fc_lockMethodTable);

	fc_CleanUpLock(fc_lock, fc_proclock,
				fc_lockMethodTable, fc_hashcode,
				fc_wakeupNeeded);

	LWLockRelease(fc_partitionLock);

	/*
	 * 减少强锁计数。此逻辑仅在2PC中需要。
	 */
	if (fc_decrement_strong_lock_count
		&& ConflictsWithRelationFastPath(fc_locktag, fc_lockmode))
	{
		uint32		fc_fasthashcode = FastPathStrongLockHashPartition(fc_hashcode);

		SpinLockAcquire(&FastPathStrongRelationLocks->mutex);
		Assert(FastPathStrongRelationLocks->count[fc_fasthashcode] > 0);
		FastPathStrongRelationLocks->count[fc_fasthashcode]--;
		SpinLockRelease(&FastPathStrongRelationLocks->mutex);
	}
}

/*
 * CheckForSessionAndXactLocks
 *		检查事务是否在同一对象上持有会话级和事务级
 *		锁；如果是，则抛出错误。
 *
 * 如果我们在同一对象上同时拥有会话级和事务级锁，
 * PREPARE TRANSACTION 必须失败。通常情况下，这种情况不会发生，
 * 因为我们只会在一些特殊操作（如 VACUUM）中在会话级别获取锁。
 * 但是在建议锁的情况下，这种情况是可能的。
 *
 * 如果我们能保持会话持有，并把事务持有转给准备的事务，那就好了。
 * 然而，这需要两个 PROCLOCK 对象，我们不能确保在 PostPrepare_Locks
 * 执行时会有另一个 PROCLOCK 可用。因此目前我们在能够安全操作时抛出错误。
 *
 * 由于 LOCALLOCK 表为每个锁模式存储一个单独的条目，
 * 我们无法通过检查 LOCALLOCK 条目来实现此检查。
 * 我们必须构建一个按 locktag 索引的临时哈希表。
 */
static void fc_CheckForSessionAndXactLocks(void)
{
	typedef struct
	{
		LOCKTAG		lock;		/* 标识可锁定对象 */
		bool		sessLock;	/* 是否持有任何会话级锁模式？ */
		bool		xactLock;	/* 是否持有任何事务级锁模式？ */
	} PerLockTagEntry;

	HASHCTL		fc_hash_ctl;
	HTAB	   *fc_lockhtab;
	HASH_SEQ_STATUS fc_status;
	LOCALLOCK  *fc_locallock;

	/* 创建一个仅按 LOCKTAG 键入的本地哈希表 */
	fc_hash_ctl.keysize = sizeof(LOCKTAG);
	fc_hash_ctl.entrysize = sizeof(PerLockTagEntry);
	fc_hash_ctl.hcxt = CurrentMemoryContext;

	fc_lockhtab = hash_create("CheckForSessionAndXactLocks table",
						   256, /* 任意初始大小 */
						   &fc_hash_ctl,
						   HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);

	/* 扫描本地锁表以查找每个 LOCKTAG 的条目 */
	hash_seq_init(&fc_status, LockMethodLocalHash);

	while ((fc_locallock = (LOCALLOCK *) hash_seq_search(&fc_status)) != NULL)
	{
		LOCALLOCKOWNER *fc_lockOwners = fc_locallock->lockOwners;
		PerLockTagEntry *fc_hentry;
		bool		fc_found;
		int			fc_i;

		/*
		 * 忽略 VXID 锁。我们不希望由准备的事务持有这些锁，
		 * 因为在重启后它们没有意义。
		 */
		if (fc_locallock->tag.lock.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
			continue;

		/* 如果我们实际上不持有锁则忽略它 */
		if (fc_locallock->nLocks <= 0)
			continue;

		/* 否则，找出或创建锁哈希表中的条目 */
		fc_hentry = (PerLockTagEntry *) hash_search(fc_lockhtab,
												 (void *) &fc_locallock->tag.lock,
												 HASH_ENTER, &fc_found);
		if (!fc_found)				/* 如果是新创建的，则初始化 */
			fc_hentry->sessLock = fc_hentry->xactLock = false;

		/* 扫描以查看我们是否在会话或事务级别或两者上持有锁 */
		for (fc_i = fc_locallock->numLockOwners - 1; fc_i >= 0; fc_i--)
		{
			if (fc_lockOwners[fc_i].owner == NULL)
				fc_hentry->sessLock = true;
			else
				fc_hentry->xactLock = true;
		}

		/*
		 * 当我们看到两种类型的锁时，可以立即抛出错误；
		 * 不需要等待是否存在更多违规情况。
		 */
		if (fc_hentry->sessLock && fc_hentry->xactLock)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot PREPARE while holding both session-level and transaction-level locks on the same object")));
	}

	/* 成功，因此清理 */
	hash_destroy(fc_lockhtab);
}

/*
 * AtPrepare_Locks
 *		为 PREPARE 做准备工作：为当前持有的所有锁
 *		创建 2PC 状态文件记录。
 *
 * 会话级锁和 VXID 锁被忽略。
 *
 * 在大多数情况下，我们不需要触碰共享内存 ---
 * 所有必要的状态信息都在 locallock 表中。
 * 然而，快速路径锁是个例外：我们在允许 PREPARE TRANSACTION 成功之前，
 * 把这些锁移动到主表中。
 */
void AtPrepare_Locks(void)
{
	HASH_SEQ_STATUS fc_status;
	LOCALLOCK  *fc_locallock;

	/* 首先，验证没有 xact 和 session 级别的锁 */
	fc_CheckForSessionAndXactLocks();

	/* 现在进行每个本地锁的清理工作 */
	hash_seq_init(&fc_status, LockMethodLocalHash);

	while ((fc_locallock = (LOCALLOCK *) hash_seq_search(&fc_status)) != NULL)
	{
		TwoPhaseLockRecord fc_record;
		LOCALLOCKOWNER *fc_lockOwners = fc_locallock->lockOwners;
		bool		fc_haveSessionLock;
		bool		fc_haveXactLock;
		int			fc_i;

		/*
		 * 忽略 VXID 锁。我们不希望由准备的事务持有这些锁，
		 * 因为在重启后它们没有意义。
		 */
		if (fc_locallock->tag.lock.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
			continue;

		/* 如果我们实际上不持有锁则忽略它 */
		if (fc_locallock->nLocks <= 0)
			continue;

		/* 扫描以查看我们是否在会话或事务级别持有它 */
		fc_haveSessionLock = fc_haveXactLock = false;
		for (fc_i = fc_locallock->numLockOwners - 1; fc_i >= 0; fc_i--)
		{
			if (fc_lockOwners[fc_i].owner == NULL)
				fc_haveSessionLock = true;
			else
				fc_haveXactLock = true;
		}

		/* 如果我们只有会话锁，则忽略它 */
		if (!fc_haveXactLock)
			continue;

		/* 这不可能发生，因为我们已经检查过它 */
		if (fc_haveSessionLock)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot PREPARE while holding both session-level and transaction-level locks on the same object")));

		/*
		 * 如果本地锁是通过快速路径获取的，我们需要将其
		 * 移动到主锁表，或者如果碰巧已经转移过，就获取
		 * 指向现有主锁表条目的指针。
		 */
		if (fc_locallock->proclock == NULL)
		{
			fc_locallock->proclock = fc_FastPathGetRelationLockEntry(fc_locallock);
			fc_locallock->lock = fc_locallock->proclock->tag.myLock;
		}

		/*
		 * 确保不释放此锁条目所持有的任何强锁计数。
		 * 我们必须保留计数，直到准备好的事务被
		 * 提交或回滚。
		 */
		fc_locallock->holdsStrongLockCount = false;

		/*
		 * 创建一个2PC记录。
		 */
		memcpy(&(fc_record.locktag), &(fc_locallock->tag.lock), sizeof(LOCKTAG));
		fc_record.lockmode = fc_locallock->tag.mode;

		RegisterTwoPhaseRecord(TWOPHASE_RM_LOCK_ID, 0,
							   &fc_record, sizeof(TwoPhaseLockRecord));
	}
}

/*
 * PostPrepare_Locks
 *		在成功PREPARE后清理
 *
 * 在这里，我们希望将锁的所有权转移到一个虚拟的PGPROC
 * ，它现在与准备的事务相关联，并且我们希望清除
 * LOCALLOCK表中的相应条目。
 *
 * 注意：通过移除LOCALLOCK条目，我们在事务的资源
 * 拥有者中留下了悬空指针。此时这样做是可以的，因为
 * resowner.c不会试图在顶层事务提交或中止时释放
 * 锁的零售。我们也可以将nLocks置为零，并将LOCALLOCK
 * 条目留给LockReleaseAll进行垃圾回收，但那可能会
 * 消耗更多的周期。
 */
void PostPrepare_Locks(TransactionId fc_xid)
{
	PGPROC	   *fc_newproc = TwoPhaseGetDummyProc(fc_xid, false);
	HASH_SEQ_STATUS fc_status;
	LOCALLOCK  *fc_locallock;
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	PROCLOCKTAG fc_proclocktag;
	int			fc_partition;

	/* 无法准备锁组跟随者。 */
	Assert(MyProc->lockGroupLeader == NULL ||
		   MyProc->lockGroupLeader == MyProc);

	/* 这是一个关键区域：任何错误都意味着大麻烦 */
	START_CRIT_SECTION();

	/*
	 * 首先我们遍历本地锁表，清理掉不需要的条目，
	 * 然后扫描进程的proclock并将其转移到目标proc。
	 *
	 * 我们分开进行这一步，因为我们可能有多个本地锁条目
	 * 指向同一个proclock，而我们不敢让它们产生悬空指针。
	 */
	hash_seq_init(&fc_status, LockMethodLocalHash);

	while ((fc_locallock = (LOCALLOCK *) hash_seq_search(&fc_status)) != NULL)
	{
		LOCALLOCKOWNER *fc_lockOwners = fc_locallock->lockOwners;
		bool		fc_haveSessionLock;
		bool		fc_haveXactLock;
		int			fc_i;

		if (fc_locallock->proclock == NULL || fc_locallock->lock == NULL)
		{
			/*
			 * 我们在尝试设置此锁时可能已耗尽共享内存。
			 * 只需忘记本地条目。
			 */
			Assert(fc_locallock->nLocks == 0);
			fc_RemoveLocalLock(fc_locallock);
			continue;
		}

		/* 忽略VXID锁 */
		if (fc_locallock->tag.lock.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
			continue;

		/* 扫描以查看我们是否在会话或事务级别持有它 */
		fc_haveSessionLock = fc_haveXactLock = false;
		for (fc_i = fc_locallock->numLockOwners - 1; fc_i >= 0; fc_i--)
		{
			if (fc_lockOwners[fc_i].owner == NULL)
				fc_haveSessionLock = true;
			else
				fc_haveXactLock = true;
		}

		/* 如果我们只有会话锁，则忽略它 */
		if (!fc_haveXactLock)
			continue;

		/* 这不可能发生，因为我们已经检查过它 */
		if (fc_haveSessionLock)
			ereport(PANIC,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot PREPARE while holding both session-level and transaction-level locks on the same object")));

		/* 标记proclock以显示我们需要释放此锁模式 */
		if (fc_locallock->nLocks > 0)
			fc_locallock->proclock->releaseMask |= LOCKBIT_ON(fc_locallock->tag.mode);

		/* 并移除locallock哈希表条目 */
		fc_RemoveLocalLock(fc_locallock);
	}

	/*
	 * 现在，分别扫描每个锁分区。
	 */
	for (fc_partition = 0; fc_partition < NUM_LOCK_PARTITIONS; fc_partition++)
	{
		LWLock	   *fc_partitionLock;
		SHM_QUEUE  *fc_procLocks = &(MyProc->myProcLocks[fc_partition]);
		PROCLOCK   *fc_nextplock;

		fc_partitionLock = LockHashPartitionLockByIndex(fc_partition);

		/*
		 * 如果此分区的proclock列表为空，我们可以跳过
		 * 获取分区锁。此优化比LockReleaseAll中的情况
		 * 更安全，因为我们在AtPrepare_Locks期间清除了
		 * 任何快速路径锁，因此现在不会有其他后端
		 * 向我们的列表中添加任何内容。为了安全起见，
		 * 我们以与LockReleaseAll相同的方式编码此逻辑。
		 */
		if (SHMQueueNext(fc_procLocks, fc_procLocks,
						 offsetof(PROCLOCK, procLink)) == NULL)
			continue;			/* 无需检查此分区 */

		LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

		for (fc_proclock = (PROCLOCK *) SHMQueueNext(fc_procLocks, fc_procLocks,
												  offsetof(PROCLOCK, procLink));
			 fc_proclock;
			 fc_proclock = fc_nextplock)
		{
			/* 首先获取链接，因为我们可能会取消链接/重新链接此proclock */
			fc_nextplock = (PROCLOCK *)
				SHMQueueNext(fc_procLocks, &fc_proclock->procLink,
							 offsetof(PROCLOCK, procLink));

			Assert(fc_proclock->tag.myProc == MyProc);

			fc_lock = fc_proclock->tag.myLock;

			/* 忽略VXID锁 */
			if (fc_lock->tag.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
				continue;

			fc_PROCLOCK_PRINT("PostPrepare_Locks", fc_proclock);
			fc_LOCK_PRINT("PostPrepare_Locks", fc_lock, 0);
			Assert(fc_lock->nRequested >= 0);
			Assert(fc_lock->nGranted >= 0);
			Assert(fc_lock->nGranted <= fc_lock->nRequested);
			Assert((fc_proclock->holdMask & ~fc_lock->grantMask) == 0);

			/* 如果没有要释放的，则忽略它（必须是会话锁） */
			if (fc_proclock->releaseMask == 0)
				continue;

			/* 否则我们应该释放所有锁 */
			if (fc_proclock->releaseMask != fc_proclock->holdMask)
				elog(PANIC, "we seem to have dropped a bit somewhere");

			/*
			 * 我们不能简单地修改proclock->tag.myProc以重新分配
			 * 锁的所有权，因为那是哈希键的一部分，proclock
			 * 将会在错误的哈希链中。相反，使用hash_update_hash_key。
			 * （我们以前创建一个新的哈希条目，但那在其他进程
			 * 繁忙创建proclock时可能会导致内存不足的失败。）我们必须
			 * 从我们的procLink链中取消链接proclock，并将其
			 * 放入新proc的链中。
			 *
			 * 注：更新后的proclock哈希键仍将属于同一哈希分区，
			 * 参见proclock_hash()。因此，我们已经持有的分区锁
			 * 对此已足够。
			 */
			SHMQueueDelete(&fc_proclock->procLink);

			/*
			 * 为proclock创建新的哈希键。
			 */
			fc_proclocktag.myLock = fc_lock;
			fc_proclocktag.myProc = fc_newproc;

			/*
			 * 更新groupLeader指针以指向新proc。 （我们最好
			 * 不属于其他人的锁组！）
			 */
			Assert(fc_proclock->groupLeader == fc_proclock->tag.myProc);
			fc_proclock->groupLeader = fc_newproc;

			/*
			 * 更新proclock。我们不应该找到与
			 * 相同哈希键的现有条目，因为对任何
			 * 给定锁来说，只能有一个条目与我自己的proc。
			 */
			if (!hash_update_hash_key(LockMethodProcLockHash,
									  (void *) fc_proclock,
									  (void *) &fc_proclocktag))
				elog(PANIC, "duplicate entry found while reassigning a prepared transaction's locks");

			/* 重新链接到新proc的proclock列表 */
			SHMQueueInsertBefore(&(fc_newproc->myProcLocks[fc_partition]),
								 &fc_proclock->procLink);

			fc_PROCLOCK_PRINT("PostPrepare_Locks: updated", fc_proclock);
		}						/* 循环遍历该分区内的PROCLOCKs */

		LWLockRelease(fc_partitionLock);
	}							/* 循环遍历分区 */

	END_CRIT_SECTION();
}


/*
 * 估计锁表所使用的共享内存空间
 */
Size LockShmemSize(void)
{
	Size		fc_size = 0;
	long		fc_max_table_size;

	/* 锁哈希表 */
	fc_max_table_size = NLOCKENTS();
	fc_size = add_size(fc_size, hash_estimate_size(fc_max_table_size, sizeof(LOCK)));

	/* proclock哈希表 */
	fc_max_table_size *= 2;
	fc_size = add_size(fc_size, hash_estimate_size(fc_max_table_size, sizeof(PROCLOCK)));

	/*
	 * 由于NLOCKENTS只是一个估计，增加10%的安全预算。
	 */
	fc_size = add_size(fc_size, fc_size / 10);

	return fc_size;
}

/*
 * GetLockStatusData - 返回锁管理器内部状态的摘要，
 * 供用户级报告函数使用。
 *
 * 返回数据由LockInstanceData对象数组组成，
 * 这些对象是PROCLOCK数据结构的轻度抽象版本，
 * 即每个唯一锁和感兴趣的PGPROC有一个条目。
 * 如果需要，调用者负责匹配相关项（例如
 * 对同一可锁定对象或PGPROC的引用）。
 *
 * 设计目标是尽可能短时间内保持LWLocks；
 * 因此，这个函数仅复制必要的数据并释放
 * 锁，允许调用者以其喜欢的时间来思考和格式化数据。
 */
LockData *
GetLockStatusData(void)
{
	LockData   *fc_data;
	PROCLOCK   *fc_proclock;
	HASH_SEQ_STATUS fc_seqstat;
	int			fc_els;
	int			fc_el;
	int			fc_i;

	fc_data = (LockData *) palloc(sizeof(LockData));

	/* 估计我们需要多少空间。 */
	fc_els = MaxBackends;
	fc_el = 0;
	fc_data->locks = (LockInstanceData *) palloc(sizeof(LockInstanceData) * fc_els);

	/*
	 * 首先，我们遍历每个后端的快速路径数组，一次锁定一个。这可能会产生系统状态的不一致图景，但同时获取所有这些LWLocks似乎不切实际（特别是，注意MAX_SIMUL_LWLOCKS）。这应该不太重要，因为这些锁都不会涉及锁冲突 - 任何可能的冲突都必须存在于主锁表中。（出于同样的原因，我们并不在乎让leaderPid完全有效。我们不能在不持有所有锁分区锁的情况下安全解引用其他后端的lockGroupLeader字段，而这样做不值得。）
*/
	for (fc_i = 0; fc_i < ProcGlobal->allProcCount; ++fc_i)
	{
		PGPROC	   *fc_proc = &ProcGlobal->allProcs[fc_i];
		uint32		fc_f;

		LWLockAcquire(&fc_proc->fpInfoLock, LW_SHARED);

		for (fc_f = 0; fc_f < FP_LOCK_SLOTS_PER_BACKEND; ++fc_f)
		{
			LockInstanceData *fc_instance;
			uint32		fc_lockbits = FAST_PATH_GET_BITS(fc_proc, fc_f);

			/* 跳过未分配的插槽。 */
			if (!fc_lockbits)
				continue;

			if (fc_el >= fc_els)
			{
				fc_els += MaxBackends;
				fc_data->locks = (LockInstanceData *)
					repalloc(fc_data->locks, sizeof(LockInstanceData) * fc_els);
			}

			fc_instance = &fc_data->locks[fc_el];
			SET_LOCKTAG_RELATION(fc_instance->locktag, fc_proc->databaseId,
								 fc_proc->fpRelId[fc_f]);
			fc_instance->holdMask = fc_lockbits << FAST_PATH_LOCKNUMBER_OFFSET;
			fc_instance->waitLockMode = NoLock;
			fc_instance->backend = fc_proc->backendId;
			fc_instance->lxid = fc_proc->lxid;
			fc_instance->pid = fc_proc->pid;
			fc_instance->leaderPid = fc_proc->pid;
			fc_instance->fastpath = true;

			/*
			 * 成功获取快速路径锁意味着没有冲突锁。
			 */
			fc_instance->waitStart = 0;

			fc_el++;
		}

		if (fc_proc->fpVXIDLock)
		{
			VirtualTransactionId fc_vxid;
			LockInstanceData *fc_instance;

			if (fc_el >= fc_els)
			{
				fc_els += MaxBackends;
				fc_data->locks = (LockInstanceData *)
					repalloc(fc_data->locks, sizeof(LockInstanceData) * fc_els);
			}

			fc_vxid.backendId = fc_proc->backendId;
			fc_vxid.localTransactionId = fc_proc->fpLocalTransactionId;

			fc_instance = &fc_data->locks[fc_el];
			SET_LOCKTAG_VIRTUALTRANSACTION(fc_instance->locktag, fc_vxid);
			fc_instance->holdMask = LOCKBIT_ON(ExclusiveLock);
			fc_instance->waitLockMode = NoLock;
			fc_instance->backend = fc_proc->backendId;
			fc_instance->lxid = fc_proc->lxid;
			fc_instance->pid = fc_proc->pid;
			fc_instance->leaderPid = fc_proc->pid;
			fc_instance->fastpath = true;
			fc_instance->waitStart = 0;

			fc_el++;
		}

		LWLockRelease(&fc_proc->fpInfoLock);
	}

	/*
	 * 接下来，获取整个共享锁数据结构的锁。我们这样做是为了至少在主锁表中的锁状态将是自一致的。
	 *
	 * 由于这是一个只读操作，我们获取共享锁而不是排他锁。这并没有太大的意义，因为所有正常操作都需要排他锁，但这也无伤大雅。它至少将允许两个后端并行执行GetLockStatusData。
	 *
	 * 必须按分区编号顺序获取LWLocks，以避免LWLock死锁。
*/
	for (fc_i = 0; fc_i < NUM_LOCK_PARTITIONS; fc_i++)
		LWLockAcquire(LockHashPartitionLockByIndex(fc_i), LW_SHARED);

	/* 现在我们可以安全地计算proclock的数量 */
	fc_data->nelements = fc_el + hash_get_num_entries(LockMethodProcLockHash);
	if (fc_data->nelements > fc_els)
	{
		fc_els = fc_data->nelements;
		fc_data->locks = (LockInstanceData *)
			repalloc(fc_data->locks, sizeof(LockInstanceData) * fc_els);
	}

	/* 现在扫描表以复制数据 */
	hash_seq_init(&fc_seqstat, LockMethodProcLockHash);

	while ((fc_proclock = (PROCLOCK *) hash_seq_search(&fc_seqstat)))
	{
		PGPROC	   *fc_proc = fc_proclock->tag.myProc;
		LOCK	   *fc_lock = fc_proclock->tag.myLock;
		LockInstanceData *fc_instance = &fc_data->locks[fc_el];

		memcpy(&fc_instance->locktag, &fc_lock->tag, sizeof(LOCKTAG));
		fc_instance->holdMask = fc_proclock->holdMask;
		if (fc_proc->waitLock == fc_proclock->tag.myLock)
			fc_instance->waitLockMode = fc_proc->waitLockMode;
		else
			fc_instance->waitLockMode = NoLock;
		fc_instance->backend = fc_proc->backendId;
		fc_instance->lxid = fc_proc->lxid;
		fc_instance->pid = fc_proc->pid;
		fc_instance->leaderPid = fc_proclock->groupLeader->pid;
		fc_instance->fastpath = false;
		fc_instance->waitStart = (TimestampTz) pg_atomic_read_u64(&fc_proc->waitStart);

		fc_el++;
	}

	/*
	 * 并释放锁。我们以相反的顺序执行此操作，有两个原因：（1）需要多个锁的其他任何人都会尝试以递增顺序锁定它们；我们不想在其他进程可以获取它所需的所有锁之前释放其他进程。（2）这避免了LWLockRelease中的O(N^2)行为。
	 */
	for (fc_i = NUM_LOCK_PARTITIONS; --fc_i >= 0;)
		LWLockRelease(LockHashPartitionLockByIndex(fc_i));

	Assert(fc_el == fc_data->nelements);

	return fc_data;
}

/*
 * GetBlockerStatusData - 返回锁管理器关于阻塞指定PID或PID的锁组中的任何成员的锁的状态摘要，以供用户级报告功能使用。
 *
 * 对于锁组中等待某些重型锁的每个PID，返回数据包括一个LockInstanceData对象数组，这些对象是GetLockStatusData使用的相同数据结构；但与该函数不同，这个函数仅报告与该PID被阻塞的锁相关的PROCLOCKs。（因此，对于任何一个被阻塞的PID，所有锁标签应该是相同的。）此外，我们返回一个数组，包含在锁的等待队列中位于被阻塞PID之前的那些后端的PID。这些可以与LockInstanceData对象中的PID进行比较，以确定哪个等待者在队列中位于被阻塞PID之前或之后。
 *
 * 如果blocked_pid不是有效的后端PID，或者其锁组中的任何内容都未在等待任何重型锁，则返回空数组。
 *
 * 设计目标是尽可能短时间内持有LWLocks；因此，这个函数只是复制必要的数据并释放锁，允许调用者随意考虑和格式化数据。
*/
BlockedProcsData *
GetBlockerStatusData(int fc_blocked_pid)
{
	BlockedProcsData *fc_data;
	PGPROC	   *fc_proc;
	int			fc_i;

	fc_data = (BlockedProcsData *) palloc(sizeof(BlockedProcsData));

	/*
	 * 估计我们需要多少空间，并进行预分配。大多数情况下，这将避免在持有LWLocks时需要进行重新分配。（我们假设，但通过Assert检查，MaxBackends的条目足够用于procs[]数组；不过其他两个可能需要扩展。）
*/
	fc_data->nprocs = fc_data->nlocks = fc_data->npids = 0;
	fc_data->maxprocs = fc_data->maxlocks = fc_data->maxpids = MaxBackends;
	fc_data->procs = (BlockedProcData *) palloc(sizeof(BlockedProcData) * fc_data->maxprocs);
	fc_data->locks = (LockInstanceData *) palloc(sizeof(LockInstanceData) * fc_data->maxlocks);
	fc_data->waiter_pids = (int *) palloc(sizeof(int) * fc_data->maxpids);

	/*
	 * 为了在ProcArray中搜索blocked_pid并假设该条目不会立即消失，我们必须持有ProcArrayLock。此外，检查任何其他后端的锁分组字段时，我们必须持有所有哈希分区锁。（对于任何一个锁组，仅一个锁是实际相关的，但我们不能提前知道是哪一个。）在此过程中持有所有这些锁相当恼人，但这并不比GetLockStatusData()更糟，并且有个好处是我们可以保证返回一个自一致的瞬时状态。
*/
	LWLockAcquire(ProcArrayLock, LW_SHARED);

	fc_proc = BackendPidGetProcWithLock(fc_blocked_pid);

	/* 如果它消失了就没什么可做的 */
	if (fc_proc != NULL)
	{
		/*
		 * 获取整个共享锁数据结构的锁。参见GetLockStatusData()中的说明。
*/
		for (fc_i = 0; fc_i < NUM_LOCK_PARTITIONS; fc_i++)
			LWLockAcquire(LockHashPartitionLockByIndex(fc_i), LW_SHARED);

		if (fc_proc->lockGroupLeader == NULL)
		{
			/* 简单情况，proc不是锁组成员 */
			fc_GetSingleProcBlockerStatusData(fc_proc, fc_data);
		}
		else
		{
			/* 检查proc的锁组中的所有procs */
			dlist_iter	fc_iter;

			dlist_foreach(fc_iter, &fc_proc->lockGroupLeader->lockGroupMembers)
			{
				PGPROC	   *fc_memberProc;

				fc_memberProc = dlist_container(PGPROC, lockGroupLink, fc_iter.cur);
				fc_GetSingleProcBlockerStatusData(fc_memberProc, fc_data);
			}
		}

		/*
		 * 并释放锁。参见GetLockStatusData()中的说明。
*/
		for (fc_i = NUM_LOCK_PARTITIONS; --fc_i >= 0;)
			LWLockRelease(LockHashPartitionLockByIndex(fc_i));

		Assert(fc_data->nprocs <= fc_data->maxprocs);
	}

	LWLockRelease(ProcArrayLock);

	return fc_data;
}

/* 收集关于可能被阻塞的proc的信息，以供GetBlockerStatusData使用 */
static void fc_GetSingleProcBlockerStatusData(PGPROC *fc_blocked_proc, BlockedProcsData *fc_data)
{
	LOCK	   *fc_theLock = fc_blocked_proc->waitLock;
	BlockedProcData *fc_bproc;
	SHM_QUEUE  *fc_procLocks;
	PROCLOCK   *fc_proclock;
	PROC_QUEUE *fc_waitQueue;
	PGPROC	   *fc_proc;
	int			fc_queue_size;
	int			fc_i;

	/* 如果这个proc没有被阻塞就没什么可做的 */
	if (fc_theLock == NULL)
		return;

	/* 设置procs[]元素 */
	fc_bproc = &fc_data->procs[fc_data->nprocs++];
	fc_bproc->pid = fc_blocked_proc->pid;
	fc_bproc->first_lock = fc_data->nlocks;
	fc_bproc->first_waiter = fc_data->npids;

	/*
	 * 我们可以忽略proc的快速路径数组，因为其中没有任何可能与争用锁相关的内容。
*/

	/* 收集与theLock相关的所有PROCLOCKs */
	fc_procLocks = &(fc_theLock->procLocks);
	fc_proclock = (PROCLOCK *) SHMQueueNext(fc_procLocks, fc_procLocks,
										 offsetof(PROCLOCK, lockLink));
	while (fc_proclock)
	{
		PGPROC	   *fc_proc = fc_proclock->tag.myProc;
		LOCK	   *fc_lock = fc_proclock->tag.myLock;
		LockInstanceData *fc_instance;

		if (fc_data->nlocks >= fc_data->maxlocks)
		{
			fc_data->maxlocks += MaxBackends;
			fc_data->locks = (LockInstanceData *)
				repalloc(fc_data->locks, sizeof(LockInstanceData) * fc_data->maxlocks);
		}

		fc_instance = &fc_data->locks[fc_data->nlocks];
		memcpy(&fc_instance->locktag, &fc_lock->tag, sizeof(LOCKTAG));
		fc_instance->holdMask = fc_proclock->holdMask;
		if (fc_proc->waitLock == fc_lock)
			fc_instance->waitLockMode = fc_proc->waitLockMode;
		else
			fc_instance->waitLockMode = NoLock;
		fc_instance->backend = fc_proc->backendId;
		fc_instance->lxid = fc_proc->lxid;
		fc_instance->pid = fc_proc->pid;
		fc_instance->leaderPid = fc_proclock->groupLeader->pid;
		fc_instance->fastpath = false;
		fc_data->nlocks++;

		fc_proclock = (PROCLOCK *) SHMQueueNext(fc_procLocks, &fc_proclock->lockLink,
											 offsetof(PROCLOCK, lockLink));
	}

	/* 如果waiter_pids[]太小而无法容纳所有等待队列PID，则扩大它 */
	fc_waitQueue = &(fc_theLock->waitProcs);
	fc_queue_size = fc_waitQueue->size;

	if (fc_queue_size > fc_data->maxpids - fc_data->npids)
	{
		fc_data->maxpids = Max(fc_data->maxpids + MaxBackends,
							fc_data->npids + fc_queue_size);
		fc_data->waiter_pids = (int *) repalloc(fc_data->waiter_pids,
											 sizeof(int) * fc_data->maxpids);
	}

	
/* 从锁的等待队列中收集 PIDs，直到阻塞的进程 */
	fc_proc = (PGPROC *) fc_waitQueue->links.next;
	for (fc_i = 0; fc_i < fc_queue_size; fc_i++)
	{
		if (fc_proc == fc_blocked_proc)
			break;
		fc_data->waiter_pids[fc_data->npids++] = fc_proc->pid;
		fc_proc = (PGPROC *) fc_proc->links.next;
	}

	fc_bproc->num_locks = fc_data->nlocks - fc_bproc->first_lock;
	fc_bproc->num_waiters = fc_data->npids - fc_bproc->first_waiter;
}

/*
 * 返回当前持有的 AccessExclusiveLocks 列表，供 LogStandbySnapshot() 使用。
 * 结果是一个 palloc 的数组，元素个数返回到 *nlocks 中。
 *
 * XXX 目前这会对锁表的所有分区加锁，但有可能做得更好。
 * 通过引用计数锁并将该值存储在每个后端的 ProcArray 条目中，
 * 我们可以判断是否需要记录任何锁，而不必获取分区锁和扫描锁表。
 * 不过，这是否值得额外的开销还是相当可疑的。
 */
xl_standby_lock *
GetRunningTransactionLocks(int *fc_nlocks)
{
	xl_standby_lock *fc_accessExclusiveLocks;
	PROCLOCK   *fc_proclock;
	HASH_SEQ_STATUS fc_seqstat;
	int			fc_i;
	int			fc_index;
	int			fc_els;

	/*
	 * 在整个共享锁数据结构上获取锁。
	 *
	 * 必须按照分区编号的顺序获取 LWLocks，以避免 LWLock 死锁。
	 */
	for (fc_i = 0; fc_i < NUM_LOCK_PARTITIONS; fc_i++)
		LWLockAcquire(LockHashPartitionLockByIndex(fc_i), LW_SHARED);

	/* 现在我们可以安全地计算proclock的数量 */
	fc_els = hash_get_num_entries(LockMethodProcLockHash);

	/*
	 * 为锁表中的所有锁分配足够的空间是多余的，
	 * 但比不得不扩大数组更方便和更快。
	 */
	fc_accessExclusiveLocks = palloc(fc_els * sizeof(xl_standby_lock));

	/* 现在扫描表以复制数据 */
	hash_seq_init(&fc_seqstat, LockMethodProcLockHash);

	/*
	 * 如果锁是当前授予的 AccessExclusiveLock，那么它只会有
	 * 一个 proclock 持有者，因此在这种特定情况下锁不会被访问两次。
	 * 不要复制此代码以用于其他地方，因为在一般情况下，这将导致
	 * 在查看非独占锁类型时出现重复锁。
	 */
	fc_index = 0;
	while ((fc_proclock = (PROCLOCK *) hash_seq_search(&fc_seqstat)))
	{
		/* 确保此定义与 LockAcquire 中使用的定义相匹配 */
		if ((fc_proclock->holdMask & LOCKBIT_ON(AccessExclusiveLock)) &&
			fc_proclock->tag.myLock->tag.locktag_type == LOCKTAG_RELATION)
		{
			PGPROC	   *fc_proc = fc_proclock->tag.myProc;
			LOCK	   *fc_lock = fc_proclock->tag.myLock;
			TransactionId fc_xid = fc_proc->xid;

			/*
			 * 如果我们知道事务已经发出了提交的 WAL 记录但尚未释放
			 * 锁，则不要记录事务的锁。仍然可能会看到已经完成的
			 * 事务持有的锁，如果它们尚未将其 xids 归零。
			 */
			if (!TransactionIdIsValid(fc_xid))
				continue;

			fc_accessExclusiveLocks[fc_index].xid = fc_xid;
			fc_accessExclusiveLocks[fc_index].dbOid = fc_lock->tag.locktag_field1;
			fc_accessExclusiveLocks[fc_index].relOid = fc_lock->tag.locktag_field2;

			fc_index++;
		}
	}

	Assert(fc_index <= fc_els);

	/*
	 * 并释放锁。我们以相反的顺序执行此操作，有两个原因：（1）需要多个锁的其他任何人都会尝试以递增顺序锁定它们；我们不想在其他进程可以获取它所需的所有锁之前释放其他进程。（2）这避免了LWLockRelease中的O(N^2)行为。
	 */
	for (fc_i = NUM_LOCK_PARTITIONS; --fc_i >= 0;)
		LWLockRelease(LockHashPartitionLockByIndex(fc_i));

	*fc_nlocks = fc_index;
	return fc_accessExclusiveLocks;
}

/* 提供任何锁模式的文本名称 */
const char * GetLockmodeName(LOCKMETHODID fc_lockmethodid, LOCKMODE fc_mode)
{
	Assert(fc_lockmethodid > 0 && fc_lockmethodid < lengthof(LockMethods));
	Assert(fc_mode > 0 && fc_mode <= LockMethods[fc_lockmethodid]->numLockModes);
	return LockMethods[fc_lockmethodid]->lockModeNames[fc_mode];
}

#ifdef LOCK_DEBUG
/*
 * 转储给定进程的 myProcLocks 列表中的所有锁。
 *
 * 调用者负责获取适当的 LWLocks。
 */
void DumpLocks(PGPROC *fc_proc)
{
	SHM_QUEUE  *fc_procLocks;
	PROCLOCK   *fc_proclock;
	LOCK	   *fc_lock;
	int			fc_i;

	if (fc_proc == NULL)
		return;

	if (fc_proc->waitLock)
		fc_LOCK_PRINT("DumpLocks: waiting on", fc_proc->waitLock, 0);

	for (fc_i = 0; fc_i < NUM_LOCK_PARTITIONS; fc_i++)
	{
		fc_procLocks = &(fc_proc->myProcLocks[fc_i]);

		fc_proclock = (PROCLOCK *) SHMQueueNext(fc_procLocks, fc_procLocks,
											 offsetof(PROCLOCK, procLink));

		while (fc_proclock)
		{
			Assert(fc_proclock->tag.myProc == fc_proc);

			fc_lock = fc_proclock->tag.myLock;

			fc_PROCLOCK_PRINT("DumpLocks", fc_proclock);
			fc_LOCK_PRINT("DumpLocks", fc_lock, 0);

			fc_proclock = (PROCLOCK *)
				SHMQueueNext(fc_procLocks, &fc_proclock->procLink,
							 offsetof(PROCLOCK, procLink));
		}
	}
}

/*
 * 转储所有 lmgr 锁。
 *
 * 调用者负责获取适当的 LWLocks。
 */
void DumpAllLocks(void)
{
	PGPROC	   *fc_proc;
	PROCLOCK   *fc_proclock;
	LOCK	   *fc_lock;
	HASH_SEQ_STATUS fc_status;

	fc_proc = MyProc;

	if (fc_proc && fc_proc->waitLock)
		fc_LOCK_PRINT("DumpAllLocks: waiting on", fc_proc->waitLock, 0);

	hash_seq_init(&fc_status, LockMethodProcLockHash);

	while ((fc_proclock = (PROCLOCK *) hash_seq_search(&fc_status)) != NULL)
	{
		fc_PROCLOCK_PRINT("DumpAllLocks", fc_proclock);

		fc_lock = fc_proclock->tag.myLock;
		if (fc_lock)
			fc_LOCK_PRINT("DumpAllLocks", fc_lock, 0);
		else
			elog(LOG, "DumpAllLocks: proclock->tag.myLock = NULL");
	}
}
#endif							/* LOCK_DEBUG */

/*
 * LOCK 2PC 资源管理器的例程
 */

/*
 * 重新获取属于已准备事务的锁。
 *
 * 因为此函数在数据库启动时运行，重新获取锁不应与正在运行的事务冲突，因为没有正在运行的事务。我们假设由存储的 2PC 文件表示的锁状态是合法的。
 *
 * 当从热备模式切换到正常操作时，锁将已被启动进程持有。为了新的进程获取锁时不检查冲突，因此我们不会在启动进程和虚拟进程之间出现冲突，尽管我们会短暂地出现两个进程持有同一 AccessExclusiveLock 的情况，这在正常情况下是不可能的，因存在冲突。如果我们处于备份模式，但尚未建立恢复快照，某些但并非所有的锁可能已经被启动进程持有。
 *
 * 这种方法简单，但也有点危险，因为如果没有足够的共享内存来获取锁，会抛出错误，该错误被提升为致命错误，恢复将中止，导致主进程崩溃。一个更安全的方法是像我们在 AtPrepare_Locks 中那样转移锁，但在热备模式下，读写后端也可能消耗掉所有共享锁内存，因此重放需要获取锁的 WAL 记录仍会抛出错误，并导致恐慌。
 */
void lock_twophase_recover(TransactionId fc_xid, uint16 fc_info,
					  void *fc_recdata, uint32 fc_len)
{
	TwoPhaseLockRecord *fc_rec = (TwoPhaseLockRecord *) fc_recdata;
	PGPROC	   *fc_proc = TwoPhaseGetDummyProc(fc_xid, false);
	LOCKTAG    *fc_locktag;
	LOCKMODE	fc_lockmode;
	LOCKMETHODID fc_lockmethodid;
	LOCK	   *fc_lock;
	PROCLOCK   *fc_proclock;
	PROCLOCKTAG fc_proclocktag;
	bool		fc_found;
	uint32		fc_hashcode;
	uint32		fc_proclock_hashcode;
	int			fc_partition;
	LWLock	   *fc_partitionLock;
	LockMethod	fc_lockMethodTable;

	Assert(fc_len == sizeof(TwoPhaseLockRecord));
	fc_locktag = &fc_rec->locktag;
	fc_lockmode = fc_rec->lockmode;
	fc_lockmethodid = fc_locktag->locktag_lockmethodid;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);
	fc_lockMethodTable = LockMethods[fc_lockmethodid];

	fc_hashcode = LockTagHashCode(fc_locktag);
	fc_partition = LockHashPartition(fc_hashcode);
	fc_partitionLock = LockHashPartitionLock(fc_hashcode);

	LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

	/*
	 * 查找或创建带有此标签的锁。
	 */
	fc_lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
												(void *) fc_locktag,
												fc_hashcode,
												HASH_ENTER_NULL,
												&fc_found);
	if (!fc_lock)
	{
		LWLockRelease(fc_partitionLock);
		ereport(ERROR,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of shared memory"),
				 errhint("You might need to increase max_locks_per_transaction.")));
	}

	/*
	 * 如果是新的锁对象，则初始化它。
	 */
	if (!fc_found)
	{
		fc_lock->grantMask = 0;
		fc_lock->waitMask = 0;
		SHMQueueInit(&(fc_lock->procLocks));
		ProcQueueInit(&(fc_lock->waitProcs));
		fc_lock->nRequested = 0;
		fc_lock->nGranted = 0;
		MemSet(fc_lock->requested, 0, sizeof(int) * MAX_LOCKMODES);
		MemSet(fc_lock->granted, 0, sizeof(int) * MAX_LOCKMODES);
		fc_LOCK_PRINT("lock_twophase_recover: new", fc_lock, fc_lockmode);
	}
	else
	{
		fc_LOCK_PRINT("lock_twophase_recover: found", fc_lock, fc_lockmode);
		Assert((fc_lock->nRequested >= 0) && (fc_lock->requested[fc_lockmode] >= 0));
		Assert((fc_lock->nGranted >= 0) && (fc_lock->granted[fc_lockmode] >= 0));
		Assert(fc_lock->nGranted <= fc_lock->nRequested);
	}

	/*
	 * 为 proclock 表创建哈希键。
	 */
	fc_proclocktag.myLock = fc_lock;
	fc_proclocktag.myProc = fc_proc;

	fc_proclock_hashcode = fc_ProcLockHashCode(&fc_proclocktag, fc_hashcode);

	/*
	 * 查找或创建带有此标签的 proclock 条目。
	 */
	fc_proclock = (PROCLOCK *) hash_search_with_hash_value(LockMethodProcLockHash,
														(void *) &fc_proclocktag,
														fc_proclock_hashcode,
														HASH_ENTER_NULL,
														&fc_found);
	if (!fc_proclock)
	{
		/* 哎呀，proclock 的共享内存不够 */
		if (fc_lock->nRequested == 0)
		{
			/*
			 * 该锁没有其他请求者，因此需要进行垃圾回收
			 * 锁对象。我们*必须*这样做以避免共享内存的永久泄漏，
			 * 因为没有任何东西可以导致后续释放锁对象。
			 */
			Assert(SHMQueueEmpty(&(fc_lock->procLocks)));
			if (!hash_search_with_hash_value(LockMethodLockHash,
											 (void *) &(fc_lock->tag),
											 fc_hashcode,
											 HASH_REMOVE,
											 NULL))
				elog(PANIC, "lock table corrupted");
		}
		LWLockRelease(fc_partitionLock);
		ereport(ERROR,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of shared memory"),
				 errhint("You might need to increase max_locks_per_transaction.")));
	}

	/*
	 * 如果是新的，初始化新的条目。
	 */
	if (!fc_found)
	{
		Assert(fc_proc->lockGroupLeader == NULL);
		fc_proclock->groupLeader = fc_proc;
		fc_proclock->holdMask = 0;
		fc_proclock->releaseMask = 0;
		/* 将 proclock 添加到适当的列表 */
		SHMQueueInsertBefore(&fc_lock->procLocks, &fc_proclock->lockLink);
		SHMQueueInsertBefore(&(fc_proc->myProcLocks[fc_partition]),
							 &fc_proclock->procLink);
		fc_PROCLOCK_PRINT("lock_twophase_recover: new", fc_proclock);
	}
	else
	{
		fc_PROCLOCK_PRINT("lock_twophase_recover: found", fc_proclock);
		Assert((fc_proclock->holdMask & ~fc_lock->grantMask) == 0);
	}

	/*
	 * lock->nRequested 和 lock->requested[] 计算请求的总数，无论是已授予的还是等待的，因此立即递增这些计数。
	 */
	fc_lock->nRequested++;
	fc_lock->requested[fc_lockmode]++;
	Assert((fc_lock->nRequested > 0) && (fc_lock->requested[fc_lockmode] > 0));

	/*
	 * 我们不应该已经持有所需的锁。
	 */
	if (fc_proclock->holdMask & LOCKBIT_ON(fc_lockmode))
		elog(ERROR, "lock %s on object %u/%u/%u is already held",
			 fc_lockMethodTable->lockModeNames[fc_lockmode],
			 fc_lock->tag.locktag_field1, fc_lock->tag.locktag_field2,
			 fc_lock->tag.locktag_field3);

	/*
	 * 我们忽略任何可能的冲突，直接授予自己锁。这不仅是因为我们懒得去考虑，还为了避免从备用模式切换到正常模式时出现死锁。请参见函数注释。
	 */
	GrantLock(fc_lock, fc_proclock, fc_lockmode);

	/*
	 * 增加强锁计数，确保任何快速路径锁请求在未咨询主锁表的情况下不会被授予。
	 */
	if (ConflictsWithRelationFastPath(&fc_lock->tag, fc_lockmode))
	{
		uint32		fc_fasthashcode = FastPathStrongLockHashPartition(fc_hashcode);

		SpinLockAcquire(&FastPathStrongRelationLocks->mutex);
		FastPathStrongRelationLocks->count[fc_fasthashcode]++;
		SpinLockRelease(&FastPathStrongRelationLocks->mutex);
	}

	LWLockRelease(fc_partitionLock);
}

/*
 * 在启动到热备模式时，重新获取属于已准备事务的锁。
 */
void lock_twophase_standby_recover(TransactionId fc_xid, uint16 fc_info,
							  void *fc_recdata, uint32 fc_len)
{
	TwoPhaseLockRecord *fc_rec = (TwoPhaseLockRecord *) fc_recdata;
	LOCKTAG    *fc_locktag;
	LOCKMODE	fc_lockmode;
	LOCKMETHODID fc_lockmethodid;

	Assert(fc_len == sizeof(TwoPhaseLockRecord));
	fc_locktag = &fc_rec->locktag;
	fc_lockmode = fc_rec->lockmode;
	fc_lockmethodid = fc_locktag->locktag_lockmethodid;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);

	if (fc_lockmode == AccessExclusiveLock &&
		fc_locktag->locktag_type == LOCKTAG_RELATION)
	{
		StandbyAcquireAccessExclusiveLock(fc_xid,
										  fc_locktag->locktag_field1 /* dboid */ ,
										  fc_locktag->locktag_field2 /* reloid */ );
	}
}


/*
 * 2PC 处理例程用于 COMMIT PREPARED 情况。
 *
 * 查找并释放由 2PC 记录指示的锁。
 */
void lock_twophase_postcommit(TransactionId fc_xid, uint16 fc_info,
						 void *fc_recdata, uint32 fc_len)
{
	TwoPhaseLockRecord *fc_rec = (TwoPhaseLockRecord *) fc_recdata;
	PGPROC	   *fc_proc = TwoPhaseGetDummyProc(fc_xid, true);
	LOCKTAG    *fc_locktag;
	LOCKMETHODID fc_lockmethodid;
	LockMethod	fc_lockMethodTable;

	Assert(fc_len == sizeof(TwoPhaseLockRecord));
	fc_locktag = &fc_rec->locktag;
	fc_lockmethodid = fc_locktag->locktag_lockmethodid;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);
	fc_lockMethodTable = LockMethods[fc_lockmethodid];

	fc_LockRefindAndRelease(fc_lockMethodTable, fc_proc, fc_locktag, fc_rec->lockmode, true);
}

/*
 * 2PC 处理例程用于 ROLLBACK PREPARED 情况。
 *
 * 实际上与 COMMIT 情况是一样的。
 */
void lock_twophase_postabort(TransactionId fc_xid, uint16 fc_info,
						void *fc_recdata, uint32 fc_len)
{
	lock_twophase_postcommit(fc_xid, fc_info, fc_recdata, fc_len);
}

/*
 *		VirtualXactLockTableInsert
 *
 *	通过快速路径获取 vxid 锁。因为我们尚未通过 ProcArray 宣告此 vxid，所以没有任何先前存在的锁。
 *
 *	由于 MyProc->fpLocalTransactionId 通常包含与 MyProc->lxid 相同的数据，您可能会想知道我们是否真的需要两个。区别在于 MyProc->lxid 是无锁设置和清除的，并由 procarray.c 检查，而 fpLocalTransactionId 受 fpInfoLock 保护，仅由锁子系统使用。这样做可以更容易地验证没有奇怪的竞态条件。
 *
 *	我们不费心将此锁记录在本地锁表中，因为它仅在事务结束时释放。相反，LockReleaseAll() 调用 VirtualXactLockTableCleanup()。
 */
void VirtualXactLockTableInsert(VirtualTransactionId fc_vxid)
{
	Assert(VirtualTransactionIdIsValid(fc_vxid));

	LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);

	Assert(MyProc->backendId == fc_vxid.backendId);
	Assert(MyProc->fpLocalTransactionId == InvalidLocalTransactionId);
	Assert(MyProc->fpVXIDLock == false);

	MyProc->fpVXIDLock = true;
	MyProc->fpLocalTransactionId = fc_vxid.localTransactionId;

	LWLockRelease(&MyProc->fpInfoLock);
}

/*
 *		VirtualXactLockTableCleanup
 *
 *	检查 VXID 锁是否已物化；如果是，释放它，解除阻塞等待者。
 */
void VirtualXactLockTableCleanup(void)
{
	bool		fc_fastpath;
	LocalTransactionId fc_lxid;

	Assert(MyProc->backendId != InvalidBackendId);

	/*
	 * 清理共享内存状态。
	 */
	LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);

	fc_fastpath = MyProc->fpVXIDLock;
	fc_lxid = MyProc->fpLocalTransactionId;
	MyProc->fpVXIDLock = false;
	MyProc->fpLocalTransactionId = InvalidLocalTransactionId;

	LWLockRelease(&MyProc->fpInfoLock);

	/*
	 * 如果 fpVXIDLock 已在不触及 fpLocalTransactionId 的情况下清除，
	 * 那么这意味着有人将锁转移到了主锁表中。
	 */
	if (!fc_fastpath && LocalTransactionIdIsValid(fc_lxid))
	{
		VirtualTransactionId fc_vxid;
		LOCKTAG		fc_locktag;

		fc_vxid.backendId = MyBackendId;
		fc_vxid.localTransactionId = fc_lxid;
		SET_LOCKTAG_VIRTUALTRANSACTION(fc_locktag, fc_vxid);

		fc_LockRefindAndRelease(LockMethods[DEFAULT_LOCKMETHOD], MyProc,
							 &fc_locktag, ExclusiveLock, false);
	}
}

/*
 *		XactLockForVirtualXact
 *
 * 如果 TransactionIdIsValid(xid)，这实质上是 XactLockTableWait(xid,
 * NULL, NULL, XLTW_None) 或 ConditionalXactLockTableWait(xid)。与这些函数不同，
 * 它假设 “xid” 永远不是子事务，并且 “xid” 是已准备、已提交或已中止的。
 *
 * 如果 !TransactionIdIsValid(xid)，则此锁定每个被称为 “vxid” 的已准备 XID，这些 XID 在其 PREPARE TRANSACTION 之前是已知的。
 */
static bool fc_XactLockForVirtualXact(VirtualTransactionId fc_vxid,
					   TransactionId fc_xid, bool fc_wait)
{
	bool		fc_more = false;

	/* 如果没有 2PC，就没有等待 2PC 的意义。 */
	if (max_prepared_xacts == 0)
		return true;

	do
	{
		LockAcquireResult fc_lar;
		LOCKTAG		fc_tag;

		/* 清除之前迭代的状态。 */
		if (fc_more)
		{
			fc_xid = InvalidTransactionId;
			fc_more = false;
		}

		/* 如果我们没有 xid，尝试查找一个。 */
		if (!TransactionIdIsValid(fc_xid))
			fc_xid = TwoPhaseGetXidByVirtualXID(fc_vxid, &fc_more);
		if (!TransactionIdIsValid(fc_xid))
		{
			Assert(!fc_more);
			return true;
		}

		/* 检查或等待 XID 完成。 */
		SET_LOCKTAG_TRANSACTION(fc_tag, fc_xid);
		fc_lar = LockAcquire(&fc_tag, ShareLock, false, !fc_wait);
		if (fc_lar == LOCKACQUIRE_NOT_AVAIL)
			return false;
		LockRelease(&fc_tag, ShareLock, false);
	} while (fc_more);

	return true;
}

/*
 *		VirtualXactLock
 *
 * 如果 wait = true，则等待直到给定的 VXID 或同一事务获取的任何 XID 仍在运行。然后，返回 true。
 *
 * 如果 wait = false，仅检查该 VXID 或这些 XID 是否仍在运行，并返回 true 或 false。
 */
bool VirtualXactLock(VirtualTransactionId fc_vxid, bool fc_wait)
{
	LOCKTAG		fc_tag;
	PGPROC	   *fc_proc;
	TransactionId fc_xid = InvalidTransactionId;

	Assert(VirtualTransactionIdIsValid(fc_vxid));

	if (VirtualTransactionIdIsRecoveredPreparedXact(fc_vxid))
		/* 没有 vxid 锁；localTransactionId 是一个正常的、锁定的 XID */
		return fc_XactLockForVirtualXact(fc_vxid, fc_vxid.localTransactionId, fc_wait);

	SET_LOCKTAG_VIRTUALTRANSACTION(fc_tag, fc_vxid);

	
/*
	 * 如果必须创建一个锁表条目，这就是代表其进行操作的PGPROC。请注意，事务可能结束，或者PGPROC可能在我们检查之前被重新分配给一个新的后端，但这无关紧要。如果我们在检查时发现相关的lxid不再这里运行，那就足以证明它在任何地方都不再运行。
	 */
	fc_proc = BackendIdGetProc(fc_vxid.backendId);
	if (fc_proc == NULL)
		return fc_XactLockForVirtualXact(fc_vxid, InvalidTransactionId, fc_wait);

	/*
	 * 在检查后端ID和lxid是否与我们等待的条目相符之前，我们必须先获取这个锁。目标后端只有在持有此锁的情况下才会设置或清除lxid。
	 */
	LWLockAcquire(&fc_proc->fpInfoLock, LW_EXCLUSIVE);

	if (fc_proc->backendId != fc_vxid.backendId
		|| fc_proc->fpLocalTransactionId != fc_vxid.localTransactionId)
	{
		/* VXID结束 */
		LWLockRelease(&fc_proc->fpInfoLock);
		return fc_XactLockForVirtualXact(fc_vxid, InvalidTransactionId, fc_wait);
	}

	/*
	 * 如果我们不被要求等待，就无需设置锁表条目。事务仍在进行中，因此只需返回false。
	 */
	if (!fc_wait)
	{
		LWLockRelease(&fc_proc->fpInfoLock);
		return false;
	}

	/*
	 * 好的，我们需要在VXID上休眠。但首先，如果需要，我们必须设置主锁表条目（即，将proc在其VXID上的快速路径锁转换为常规锁）。
	 */
	if (fc_proc->fpVXIDLock)
	{
		PROCLOCK   *fc_proclock;
		uint32		fc_hashcode;
		LWLock	   *fc_partitionLock;

		fc_hashcode = LockTagHashCode(&fc_tag);

		fc_partitionLock = LockHashPartitionLock(fc_hashcode);
		LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

		fc_proclock = fc_SetupLockInTable(LockMethods[DEFAULT_LOCKMETHOD], fc_proc,
									&fc_tag, fc_hashcode, ExclusiveLock);
		if (!fc_proclock)
		{
			LWLockRelease(fc_partitionLock);
			LWLockRelease(&fc_proc->fpInfoLock);
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of shared memory"),
					 errhint("You might need to increase max_locks_per_transaction.")));
#ifdef FDD //cppcheck
			return false;
#endif
		}
		GrantLock(fc_proclock->tag.myLock, fc_proclock, ExclusiveLock);

		LWLockRelease(fc_partitionLock);

		fc_proc->fpVXIDLock = false;
	}

	/*
	 * 如果proc现在有一个XID，我们将避免进行TwoPhaseGetXidByVirtualXID()搜索。proc可能已经分配了这个XID，但尚未锁定它，在这种情况下，proc会在释放VXID之前锁定这个XID。fpInfoLock临界区排除了VirtualXactLockTableCleanup()，因此我们不会保存不同VXID的XID。我们保存这个XID是在设置主锁表条目之前还是之后并不重要。
	 */
	fc_xid = fc_proc->xid;

	/* 完成proc->fpLockBits */
	LWLockRelease(&fc_proc->fpInfoLock);

	/* 该等待时间。 */
	(void) LockAcquire(&fc_tag, ShareLock, false, false);

	LockRelease(&fc_tag, ShareLock, false);
	return fc_XactLockForVirtualXact(fc_vxid, fc_xid, fc_wait);
}

/*
 * LockWaiterCount
 *
 * 查找此锁标签上的锁请求者数量
 */
int LockWaiterCount(const LOCKTAG *fc_locktag)
{
	LOCKMETHODID fc_lockmethodid = fc_locktag->locktag_lockmethodid;
	LOCK	   *fc_lock;
	bool		fc_found;
	uint32		fc_hashcode;
	LWLock	   *fc_partitionLock;
	int			fc_waiters = 0;

	if (fc_lockmethodid <= 0 || fc_lockmethodid >= lengthof(LockMethods))
		elog(ERROR, "unrecognized lock method: %d", fc_lockmethodid);

	fc_hashcode = LockTagHashCode(fc_locktag);
	fc_partitionLock = LockHashPartitionLock(fc_hashcode);
	LWLockAcquire(fc_partitionLock, LW_EXCLUSIVE);

	fc_lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
												(const void *) fc_locktag,
												fc_hashcode,
												HASH_FIND,
												&fc_found);
	if (fc_found)
	{
		Assert(fc_lock != NULL);
		fc_waiters = fc_lock->nRequested;
	}
	LWLockRelease(fc_partitionLock);

	return fc_waiters;
}
